From 1b4c390ac8a2ac5b91186ac1ed27ecdd76142cf7 Mon Sep 17 00:00:00 2001 From: yzt Date: Thu, 10 Oct 2024 13:40:49 +0800 Subject: [PATCH] Support handling function return value from worker process (#46158) Fix Azure/azure-functions-dotnet-worker#1496 The reason to add .NET 6 and .NET 8 as TFM: The worker function return value is a Newtonsoft JObject if it's an object in the worker. Since .NET Core Frameworks, the default SignalR JSON protocol uses System.Text.Json library and can't serialize Newtonsoft JObject. Therefore, we need to wrap the JObject to a System.Text.Json serializable object with a customized JSON converter. The customized JSON converter first converts the Newtonsoft JObject to JSON with Newtonsoft library, then writes the raw JSON object with System.Text.Json writer. For .NET Standard 2.0, it's not possible to write a raw JSON with System.Text.Json writer, therefore, we need to add .NET 6 and .NET 8 as TFM to write the raw JSON. --- .../CHANGELOG.md | 9 +- ...ebJobs.Extensions.SignalRService.net6.0.cs | 302 ++++++++++++++++++ ...ebJobs.Extensions.SignalRService.net8.0.cs | 302 ++++++++++++++++++ ...xtensions.SignalRService.netstandard2.0.cs | 4 +- .../src/Config/SignalRConfigProvider.cs | 2 +- ...e.WebJobs.Extensions.SignalRService.csproj | 5 +- .../SignalRInvocationMethodExecutor.cs | 15 +- .../SignalRTriggerAttribute.cs | 6 +- .../TriggerBindings/SignalRTriggerBinding.cs | 8 +- .../TriggerBindings/Utils/JTokenWrapper.cs | 47 +++ .../DefaultSecurityTokenValidatorTests.cs | 2 +- .../tests/Utils/TestHelpers.cs | 10 +- 12 files changed, 689 insertions(+), 23 deletions(-) create mode 100644 sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/api/Microsoft.Azure.WebJobs.Extensions.SignalRService.net6.0.cs create mode 100644 sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/api/Microsoft.Azure.WebJobs.Extensions.SignalRService.net8.0.cs create mode 100644 sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/Utils/JTokenWrapper.cs diff --git a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/CHANGELOG.md b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/CHANGELOG.md index 3e16425e487b6..1aa8bf6a73902 100644 --- a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/CHANGELOG.md +++ b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/CHANGELOG.md @@ -1,14 +1,9 @@ # Release History -## 1.15.0-beta.1 (Unreleased) - -### Features Added - -### Breaking Changes +## 1.15.0 (2024-10-14) ### Bugs Fixed - -### Other Changes +* Fixed the issue that the function return value from isolated-worker process is not handled correctly. ## 1.14.0 (2024-05-24) diff --git a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/api/Microsoft.Azure.WebJobs.Extensions.SignalRService.net6.0.cs b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/api/Microsoft.Azure.WebJobs.Extensions.SignalRService.net6.0.cs new file mode 100644 index 0000000000000..6dd1355221126 --- /dev/null +++ b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/api/Microsoft.Azure.WebJobs.Extensions.SignalRService.net6.0.cs @@ -0,0 +1,302 @@ +namespace Microsoft.Azure.WebJobs.Extensions.SignalRService +{ + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public static partial class Category + { + public const string Connections = "connections"; + public const string Messages = "messages"; + } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public static partial class Event + { + public const string Connected = "connected"; + public const string Disconnected = "disconnected"; + } + [Newtonsoft.Json.JsonConverterAttribute(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public enum GroupAction + { + [System.Runtime.Serialization.EnumMemberAttribute(Value="add")] + Add = 0, + [System.Runtime.Serialization.EnumMemberAttribute(Value="remove")] + Remove = 1, + [System.Runtime.Serialization.EnumMemberAttribute(Value="removeAll")] + RemoveAll = 2, + } + public partial class InvocationContext + { + public InvocationContext() { } + public object[] Arguments { get { throw null; } set { } } + public string Category { get { throw null; } set { } } + public System.Collections.Generic.IDictionary Claims { get { throw null; } set { } } + public string ConnectionId { get { throw null; } set { } } + public string Error { get { throw null; } set { } } + public string Event { get { throw null; } set { } } + public System.Collections.Generic.IDictionary Headers { get { throw null; } set { } } + public string Hub { get { throw null; } set { } } + public System.Collections.Generic.IDictionary Query { get { throw null; } set { } } + public string UserId { get { throw null; } set { } } + } + public static partial class InvocationContextExtensions + { + public static Microsoft.Azure.SignalR.Management.ClientManager GetClientManager(this Microsoft.Azure.WebJobs.Extensions.SignalRService.InvocationContext invocationContext) { throw null; } + public static System.Threading.Tasks.Task GetClientsAsync(this Microsoft.Azure.WebJobs.Extensions.SignalRService.InvocationContext invocationContext) { throw null; } + public static System.Threading.Tasks.Task GetGroupsAsync(this Microsoft.Azure.WebJobs.Extensions.SignalRService.InvocationContext invocationContext) { throw null; } + public static System.Threading.Tasks.Task GetUserGroupManagerAsync(this Microsoft.Azure.WebJobs.Extensions.SignalRService.InvocationContext invocationContext) { throw null; } + } + public partial interface ISecurityTokenValidator + { + Microsoft.Azure.WebJobs.Extensions.SignalRService.SecurityTokenResult ValidateToken(Microsoft.AspNetCore.Http.HttpRequest request); + } + public partial interface IServiceHubContextStore + { + Microsoft.Azure.SignalR.Management.IServiceManager ServiceManager { get; } + System.Threading.Tasks.ValueTask GetAsync(string hubName); + } + public partial interface ISignalRConnectionInfoConfigurer + { + System.Func Configure { get; set; } + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter | System.AttributeTargets.ReturnValue)] + public abstract partial class NegotiationBaseAttribute : System.Attribute + { + protected NegotiationBaseAttribute() { } + public string[] ClaimTypeList { get { throw null; } set { } } + public string ConnectionStringSetting { get { throw null; } set { } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string HubName { get { throw null; } set { } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string IdToken { get { throw null; } set { } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string UserId { get { throw null; } set { } } + } + public sealed partial class SecurityTokenResult + { + internal SecurityTokenResult() { } + [Newtonsoft.Json.JsonPropertyAttribute("exception")] + public System.Exception Exception { get { throw null; } } + public System.Security.Claims.ClaimsPrincipal Principal { get { throw null; } } + [Newtonsoft.Json.JsonPropertyAttribute("status")] + public Microsoft.Azure.WebJobs.Extensions.SignalRService.SecurityTokenStatus Status { get { throw null; } } + public static Microsoft.Azure.WebJobs.Extensions.SignalRService.SecurityTokenResult Empty() { throw null; } + public static Microsoft.Azure.WebJobs.Extensions.SignalRService.SecurityTokenResult Error(System.Exception ex) { throw null; } + public static Microsoft.Azure.WebJobs.Extensions.SignalRService.SecurityTokenResult Success(System.Security.Claims.ClaimsPrincipal principal) { throw null; } + } + public enum SecurityTokenStatus + { + Valid = 0, + Error = 1, + Empty = 2, + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter | System.AttributeTargets.ReturnValue)] + public partial class SecurityTokenValidationAttribute : System.Attribute + { + public SecurityTokenValidationAttribute() { } + } + public abstract partial class ServerlessHub : System.IDisposable + { + protected ServerlessHub(Microsoft.Azure.SignalR.Management.IServiceHubContext hubContext = null, Microsoft.Azure.SignalR.Management.IServiceManager serviceManager = null) { } + public Microsoft.Azure.SignalR.Management.ClientManager ClientManager { get { throw null; } } + public Microsoft.AspNetCore.SignalR.IHubClients Clients { get { throw null; } } + public Microsoft.AspNetCore.SignalR.IGroupManager Groups { get { throw null; } } + public string HubName { get { throw null; } } + public Microsoft.Azure.SignalR.Management.IUserGroupManager UserGroups { get { throw null; } } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + protected System.Collections.Generic.IList GetClaims(string jwt) { throw null; } + protected Microsoft.Azure.WebJobs.Extensions.SignalRService.SignalRConnectionInfo Negotiate(string userId = null, System.Collections.Generic.IList claims = null, System.TimeSpan? lifetime = default(System.TimeSpan?)) { throw null; } + protected System.Threading.Tasks.Task NegotiateAsync(Microsoft.Azure.SignalR.Management.NegotiationOptions options) { throw null; } + [System.AttributeUsageAttribute(System.AttributeTargets.Class)] + protected internal partial class SignalRConnectionAttribute : System.Attribute, Microsoft.Azure.WebJobs.IConnectionProvider + { + public SignalRConnectionAttribute(string connectionStringSetting) { } + public string Connection { get { throw null; } set { } } + } + } + public abstract partial class ServerlessHub where T : class + { + protected ServerlessHub() { } + protected ServerlessHub(Microsoft.Azure.SignalR.Management.ServiceHubContext serviceHubContext) { } + public Microsoft.Azure.SignalR.Management.ClientManager ClientManager { get { throw null; } } + public Microsoft.AspNetCore.SignalR.IHubClients Clients { get { throw null; } } + public Microsoft.Azure.SignalR.Management.GroupManager Groups { get { throw null; } } + public string HubName { get { throw null; } } + public Microsoft.Azure.SignalR.Management.UserGroupManager UserGroups { get { throw null; } } + protected System.Collections.Generic.IList GetClaims(string jwt) { throw null; } + protected System.Threading.Tasks.Task NegotiateAsync(Microsoft.Azure.SignalR.Management.NegotiationOptions options) { throw null; } + } + public partial class SignalRAsyncCollector : Microsoft.Azure.WebJobs.IAsyncCollector + { + internal SignalRAsyncCollector() { } + public System.Threading.Tasks.Task AddAsync(T item, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter | System.AttributeTargets.ReturnValue)] + public partial class SignalRAttribute : System.Attribute + { + public SignalRAttribute() { } + public string ConnectionStringSetting { get { throw null; } set { } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string HubName { get { throw null; } set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class)] + public partial class SignalRConnectionAttribute : System.Attribute, Microsoft.Azure.WebJobs.IConnectionProvider + { + public SignalRConnectionAttribute(string connection) { } + public string Connection { get { throw null; } set { } } + } + public partial class SignalRConnectionDetail + { + public SignalRConnectionDetail() { } + public System.Collections.Generic.IList Claims { get { throw null; } set { } } + public string UserId { get { throw null; } set { } } + } + public partial class SignalRConnectionInfo + { + public SignalRConnectionInfo() { } + [Newtonsoft.Json.JsonPropertyAttribute("accessToken")] + public string AccessToken { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("url")] + public string Url { get { throw null; } set { } } + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter | System.AttributeTargets.ReturnValue)] + public partial class SignalRConnectionInfoAttribute : Microsoft.Azure.WebJobs.Extensions.SignalRService.NegotiationBaseAttribute + { + public SignalRConnectionInfoAttribute() { } + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter)] + public partial class SignalREndpointsAttribute : System.Attribute + { + public SignalREndpointsAttribute() { } + public string ConnectionStringSetting { get { throw null; } set { } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string HubName { get { throw null; } set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=true, Inherited=true)] + public abstract partial class SignalRFilterAttribute : Microsoft.Azure.WebJobs.Host.FunctionInvocationFilterAttribute + { + protected SignalRFilterAttribute() { } + public abstract System.Threading.Tasks.Task FilterAsync(Microsoft.Azure.WebJobs.Extensions.SignalRService.InvocationContext invocationContext, System.Threading.CancellationToken cancellationToken); + public override System.Threading.Tasks.Task OnExecutingAsync(Microsoft.Azure.WebJobs.Host.FunctionExecutingContext executingContext, System.Threading.CancellationToken cancellationToken) { throw null; } + } + public static partial class SignalRFunctionsHostBuilderExtensions + { + public static Microsoft.Azure.Functions.Extensions.DependencyInjection.IFunctionsHostBuilder AddDefaultAuth(this Microsoft.Azure.Functions.Extensions.DependencyInjection.IFunctionsHostBuilder builder, System.Action configureTokenValidationParameters, System.Func configurer = null) { throw null; } + } + [Newtonsoft.Json.JsonObjectAttribute] + public partial class SignalRGroupAction + { + public SignalRGroupAction() { } + [Newtonsoft.Json.JsonPropertyAttribute("action")] + [Newtonsoft.Json.JsonRequiredAttribute] + public Microsoft.Azure.WebJobs.Extensions.SignalRService.GroupAction Action { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("connectionId")] + public string ConnectionId { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("endpoints")] + public Microsoft.Azure.SignalR.ServiceEndpoint[] Endpoints { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("groupName")] + public string GroupName { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("userId")] + public string UserId { get { throw null; } set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter)] + public partial class SignalRIgnoreAttribute : System.Attribute + { + public SignalRIgnoreAttribute() { } + } + [Newtonsoft.Json.JsonObjectAttribute] + public partial class SignalRMessage + { + public SignalRMessage() { } + [Newtonsoft.Json.JsonPropertyAttribute("arguments")] + [Newtonsoft.Json.JsonRequiredAttribute] + public object[] Arguments { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("connectionId")] + public string ConnectionId { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("endpoints")] + public Microsoft.Azure.SignalR.ServiceEndpoint[] Endpoints { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("groupName")] + public string GroupName { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("target")] + [Newtonsoft.Json.JsonRequiredAttribute] + public string Target { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("userId")] + public string UserId { get { throw null; } set { } } + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter)] + public partial class SignalRNegotiationAttribute : Microsoft.Azure.WebJobs.Extensions.SignalRService.NegotiationBaseAttribute + { + public SignalRNegotiationAttribute() { } + } + public partial class SignalROptions : Microsoft.Azure.WebJobs.Hosting.IOptionsFormatter + { + public SignalROptions() { } + public System.TimeSpan? HttpClientTimeout { get { throw null; } set { } } + public Azure.Core.Serialization.ObjectSerializer? JsonObjectSerializer { get { throw null; } set { } } + public Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol? MessagePackHubProtocol { get { throw null; } set { } } + public Microsoft.Azure.SignalR.Management.ServiceManagerRetryOptions? RetryOptions { get { throw null; } set { } } + public System.Collections.Generic.IList ServiceEndpoints { get { throw null; } } + public Microsoft.Azure.SignalR.Management.ServiceTransportType ServiceTransportType { get { throw null; } set { } } + string Microsoft.Azure.WebJobs.Hosting.IOptionsFormatter.Format() { throw null; } + } + public partial class SignalROutputConverter + { + public SignalROutputConverter() { } + public object ConvertToSignalROutput(object input) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter)] + public partial class SignalRParameterAttribute : System.Attribute + { + public SignalRParameterAttribute() { } + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute(TriggerHandlesReturnValue=true)] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter)] + public partial class SignalRTriggerAttribute : System.Attribute + { + public SignalRTriggerAttribute() { } + public SignalRTriggerAttribute(string hubName, string category, string @event) { } + public SignalRTriggerAttribute(string hubName, string category, string @event, params string[] parameterNames) { } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string Category { get { throw null; } } + public string ConnectionStringSetting { get { throw null; } set { } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string Event { get { throw null; } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string HubName { get { throw null; } } + public string[] ParameterNames { get { throw null; } } + } + public static partial class SignalRTriggerCategories + { + public const string Connections = "connections"; + public const string Messages = "messages"; + } + public static partial class SignalRTriggerEvents + { + public const string Connected = "connected"; + public const string Disconnected = "disconnected"; + } + public partial class SignalRTriggerException : System.Exception + { + public SignalRTriggerException() { } + public SignalRTriggerException(string message) { } + public SignalRTriggerException(string message, System.Exception innerException) { } + } + public static partial class SignalRWebJobsBuilderExtensions + { + public static Microsoft.Azure.WebJobs.IWebJobsBuilder AddSignalR(this Microsoft.Azure.WebJobs.IWebJobsBuilder builder) { throw null; } + } + public partial class SignalRWebJobsStartup : Microsoft.Azure.WebJobs.Hosting.IWebJobsStartup + { + public SignalRWebJobsStartup() { } + public void Configure(Microsoft.Azure.WebJobs.IWebJobsBuilder builder) { } + } + public static partial class StaticServiceHubContextStore + { + public static Microsoft.Azure.WebJobs.Extensions.SignalRService.IServiceHubContextStore Get(string connectionStringSetting = "AzureSignalRConnectionString") { throw null; } + } +} diff --git a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/api/Microsoft.Azure.WebJobs.Extensions.SignalRService.net8.0.cs b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/api/Microsoft.Azure.WebJobs.Extensions.SignalRService.net8.0.cs new file mode 100644 index 0000000000000..6dd1355221126 --- /dev/null +++ b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/api/Microsoft.Azure.WebJobs.Extensions.SignalRService.net8.0.cs @@ -0,0 +1,302 @@ +namespace Microsoft.Azure.WebJobs.Extensions.SignalRService +{ + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public static partial class Category + { + public const string Connections = "connections"; + public const string Messages = "messages"; + } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public static partial class Event + { + public const string Connected = "connected"; + public const string Disconnected = "disconnected"; + } + [Newtonsoft.Json.JsonConverterAttribute(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public enum GroupAction + { + [System.Runtime.Serialization.EnumMemberAttribute(Value="add")] + Add = 0, + [System.Runtime.Serialization.EnumMemberAttribute(Value="remove")] + Remove = 1, + [System.Runtime.Serialization.EnumMemberAttribute(Value="removeAll")] + RemoveAll = 2, + } + public partial class InvocationContext + { + public InvocationContext() { } + public object[] Arguments { get { throw null; } set { } } + public string Category { get { throw null; } set { } } + public System.Collections.Generic.IDictionary Claims { get { throw null; } set { } } + public string ConnectionId { get { throw null; } set { } } + public string Error { get { throw null; } set { } } + public string Event { get { throw null; } set { } } + public System.Collections.Generic.IDictionary Headers { get { throw null; } set { } } + public string Hub { get { throw null; } set { } } + public System.Collections.Generic.IDictionary Query { get { throw null; } set { } } + public string UserId { get { throw null; } set { } } + } + public static partial class InvocationContextExtensions + { + public static Microsoft.Azure.SignalR.Management.ClientManager GetClientManager(this Microsoft.Azure.WebJobs.Extensions.SignalRService.InvocationContext invocationContext) { throw null; } + public static System.Threading.Tasks.Task GetClientsAsync(this Microsoft.Azure.WebJobs.Extensions.SignalRService.InvocationContext invocationContext) { throw null; } + public static System.Threading.Tasks.Task GetGroupsAsync(this Microsoft.Azure.WebJobs.Extensions.SignalRService.InvocationContext invocationContext) { throw null; } + public static System.Threading.Tasks.Task GetUserGroupManagerAsync(this Microsoft.Azure.WebJobs.Extensions.SignalRService.InvocationContext invocationContext) { throw null; } + } + public partial interface ISecurityTokenValidator + { + Microsoft.Azure.WebJobs.Extensions.SignalRService.SecurityTokenResult ValidateToken(Microsoft.AspNetCore.Http.HttpRequest request); + } + public partial interface IServiceHubContextStore + { + Microsoft.Azure.SignalR.Management.IServiceManager ServiceManager { get; } + System.Threading.Tasks.ValueTask GetAsync(string hubName); + } + public partial interface ISignalRConnectionInfoConfigurer + { + System.Func Configure { get; set; } + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter | System.AttributeTargets.ReturnValue)] + public abstract partial class NegotiationBaseAttribute : System.Attribute + { + protected NegotiationBaseAttribute() { } + public string[] ClaimTypeList { get { throw null; } set { } } + public string ConnectionStringSetting { get { throw null; } set { } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string HubName { get { throw null; } set { } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string IdToken { get { throw null; } set { } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string UserId { get { throw null; } set { } } + } + public sealed partial class SecurityTokenResult + { + internal SecurityTokenResult() { } + [Newtonsoft.Json.JsonPropertyAttribute("exception")] + public System.Exception Exception { get { throw null; } } + public System.Security.Claims.ClaimsPrincipal Principal { get { throw null; } } + [Newtonsoft.Json.JsonPropertyAttribute("status")] + public Microsoft.Azure.WebJobs.Extensions.SignalRService.SecurityTokenStatus Status { get { throw null; } } + public static Microsoft.Azure.WebJobs.Extensions.SignalRService.SecurityTokenResult Empty() { throw null; } + public static Microsoft.Azure.WebJobs.Extensions.SignalRService.SecurityTokenResult Error(System.Exception ex) { throw null; } + public static Microsoft.Azure.WebJobs.Extensions.SignalRService.SecurityTokenResult Success(System.Security.Claims.ClaimsPrincipal principal) { throw null; } + } + public enum SecurityTokenStatus + { + Valid = 0, + Error = 1, + Empty = 2, + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter | System.AttributeTargets.ReturnValue)] + public partial class SecurityTokenValidationAttribute : System.Attribute + { + public SecurityTokenValidationAttribute() { } + } + public abstract partial class ServerlessHub : System.IDisposable + { + protected ServerlessHub(Microsoft.Azure.SignalR.Management.IServiceHubContext hubContext = null, Microsoft.Azure.SignalR.Management.IServiceManager serviceManager = null) { } + public Microsoft.Azure.SignalR.Management.ClientManager ClientManager { get { throw null; } } + public Microsoft.AspNetCore.SignalR.IHubClients Clients { get { throw null; } } + public Microsoft.AspNetCore.SignalR.IGroupManager Groups { get { throw null; } } + public string HubName { get { throw null; } } + public Microsoft.Azure.SignalR.Management.IUserGroupManager UserGroups { get { throw null; } } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + protected System.Collections.Generic.IList GetClaims(string jwt) { throw null; } + protected Microsoft.Azure.WebJobs.Extensions.SignalRService.SignalRConnectionInfo Negotiate(string userId = null, System.Collections.Generic.IList claims = null, System.TimeSpan? lifetime = default(System.TimeSpan?)) { throw null; } + protected System.Threading.Tasks.Task NegotiateAsync(Microsoft.Azure.SignalR.Management.NegotiationOptions options) { throw null; } + [System.AttributeUsageAttribute(System.AttributeTargets.Class)] + protected internal partial class SignalRConnectionAttribute : System.Attribute, Microsoft.Azure.WebJobs.IConnectionProvider + { + public SignalRConnectionAttribute(string connectionStringSetting) { } + public string Connection { get { throw null; } set { } } + } + } + public abstract partial class ServerlessHub where T : class + { + protected ServerlessHub() { } + protected ServerlessHub(Microsoft.Azure.SignalR.Management.ServiceHubContext serviceHubContext) { } + public Microsoft.Azure.SignalR.Management.ClientManager ClientManager { get { throw null; } } + public Microsoft.AspNetCore.SignalR.IHubClients Clients { get { throw null; } } + public Microsoft.Azure.SignalR.Management.GroupManager Groups { get { throw null; } } + public string HubName { get { throw null; } } + public Microsoft.Azure.SignalR.Management.UserGroupManager UserGroups { get { throw null; } } + protected System.Collections.Generic.IList GetClaims(string jwt) { throw null; } + protected System.Threading.Tasks.Task NegotiateAsync(Microsoft.Azure.SignalR.Management.NegotiationOptions options) { throw null; } + } + public partial class SignalRAsyncCollector : Microsoft.Azure.WebJobs.IAsyncCollector + { + internal SignalRAsyncCollector() { } + public System.Threading.Tasks.Task AddAsync(T item, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter | System.AttributeTargets.ReturnValue)] + public partial class SignalRAttribute : System.Attribute + { + public SignalRAttribute() { } + public string ConnectionStringSetting { get { throw null; } set { } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string HubName { get { throw null; } set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class)] + public partial class SignalRConnectionAttribute : System.Attribute, Microsoft.Azure.WebJobs.IConnectionProvider + { + public SignalRConnectionAttribute(string connection) { } + public string Connection { get { throw null; } set { } } + } + public partial class SignalRConnectionDetail + { + public SignalRConnectionDetail() { } + public System.Collections.Generic.IList Claims { get { throw null; } set { } } + public string UserId { get { throw null; } set { } } + } + public partial class SignalRConnectionInfo + { + public SignalRConnectionInfo() { } + [Newtonsoft.Json.JsonPropertyAttribute("accessToken")] + public string AccessToken { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("url")] + public string Url { get { throw null; } set { } } + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter | System.AttributeTargets.ReturnValue)] + public partial class SignalRConnectionInfoAttribute : Microsoft.Azure.WebJobs.Extensions.SignalRService.NegotiationBaseAttribute + { + public SignalRConnectionInfoAttribute() { } + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter)] + public partial class SignalREndpointsAttribute : System.Attribute + { + public SignalREndpointsAttribute() { } + public string ConnectionStringSetting { get { throw null; } set { } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string HubName { get { throw null; } set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Method, AllowMultiple=true, Inherited=true)] + public abstract partial class SignalRFilterAttribute : Microsoft.Azure.WebJobs.Host.FunctionInvocationFilterAttribute + { + protected SignalRFilterAttribute() { } + public abstract System.Threading.Tasks.Task FilterAsync(Microsoft.Azure.WebJobs.Extensions.SignalRService.InvocationContext invocationContext, System.Threading.CancellationToken cancellationToken); + public override System.Threading.Tasks.Task OnExecutingAsync(Microsoft.Azure.WebJobs.Host.FunctionExecutingContext executingContext, System.Threading.CancellationToken cancellationToken) { throw null; } + } + public static partial class SignalRFunctionsHostBuilderExtensions + { + public static Microsoft.Azure.Functions.Extensions.DependencyInjection.IFunctionsHostBuilder AddDefaultAuth(this Microsoft.Azure.Functions.Extensions.DependencyInjection.IFunctionsHostBuilder builder, System.Action configureTokenValidationParameters, System.Func configurer = null) { throw null; } + } + [Newtonsoft.Json.JsonObjectAttribute] + public partial class SignalRGroupAction + { + public SignalRGroupAction() { } + [Newtonsoft.Json.JsonPropertyAttribute("action")] + [Newtonsoft.Json.JsonRequiredAttribute] + public Microsoft.Azure.WebJobs.Extensions.SignalRService.GroupAction Action { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("connectionId")] + public string ConnectionId { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("endpoints")] + public Microsoft.Azure.SignalR.ServiceEndpoint[] Endpoints { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("groupName")] + public string GroupName { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("userId")] + public string UserId { get { throw null; } set { } } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter)] + public partial class SignalRIgnoreAttribute : System.Attribute + { + public SignalRIgnoreAttribute() { } + } + [Newtonsoft.Json.JsonObjectAttribute] + public partial class SignalRMessage + { + public SignalRMessage() { } + [Newtonsoft.Json.JsonPropertyAttribute("arguments")] + [Newtonsoft.Json.JsonRequiredAttribute] + public object[] Arguments { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("connectionId")] + public string ConnectionId { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("endpoints")] + public Microsoft.Azure.SignalR.ServiceEndpoint[] Endpoints { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("groupName")] + public string GroupName { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("target")] + [Newtonsoft.Json.JsonRequiredAttribute] + public string Target { get { throw null; } set { } } + [Newtonsoft.Json.JsonPropertyAttribute("userId")] + public string UserId { get { throw null; } set { } } + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter)] + public partial class SignalRNegotiationAttribute : Microsoft.Azure.WebJobs.Extensions.SignalRService.NegotiationBaseAttribute + { + public SignalRNegotiationAttribute() { } + } + public partial class SignalROptions : Microsoft.Azure.WebJobs.Hosting.IOptionsFormatter + { + public SignalROptions() { } + public System.TimeSpan? HttpClientTimeout { get { throw null; } set { } } + public Azure.Core.Serialization.ObjectSerializer? JsonObjectSerializer { get { throw null; } set { } } + public Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol? MessagePackHubProtocol { get { throw null; } set { } } + public Microsoft.Azure.SignalR.Management.ServiceManagerRetryOptions? RetryOptions { get { throw null; } set { } } + public System.Collections.Generic.IList ServiceEndpoints { get { throw null; } } + public Microsoft.Azure.SignalR.Management.ServiceTransportType ServiceTransportType { get { throw null; } set { } } + string Microsoft.Azure.WebJobs.Hosting.IOptionsFormatter.Format() { throw null; } + } + public partial class SignalROutputConverter + { + public SignalROutputConverter() { } + public object ConvertToSignalROutput(object input) { throw null; } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter)] + public partial class SignalRParameterAttribute : System.Attribute + { + public SignalRParameterAttribute() { } + } + [Microsoft.Azure.WebJobs.Description.BindingAttribute(TriggerHandlesReturnValue=true)] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter)] + public partial class SignalRTriggerAttribute : System.Attribute + { + public SignalRTriggerAttribute() { } + public SignalRTriggerAttribute(string hubName, string category, string @event) { } + public SignalRTriggerAttribute(string hubName, string category, string @event, params string[] parameterNames) { } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string Category { get { throw null; } } + public string ConnectionStringSetting { get { throw null; } set { } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string Event { get { throw null; } } + [Microsoft.Azure.WebJobs.Description.AutoResolveAttribute] + public string HubName { get { throw null; } } + public string[] ParameterNames { get { throw null; } } + } + public static partial class SignalRTriggerCategories + { + public const string Connections = "connections"; + public const string Messages = "messages"; + } + public static partial class SignalRTriggerEvents + { + public const string Connected = "connected"; + public const string Disconnected = "disconnected"; + } + public partial class SignalRTriggerException : System.Exception + { + public SignalRTriggerException() { } + public SignalRTriggerException(string message) { } + public SignalRTriggerException(string message, System.Exception innerException) { } + } + public static partial class SignalRWebJobsBuilderExtensions + { + public static Microsoft.Azure.WebJobs.IWebJobsBuilder AddSignalR(this Microsoft.Azure.WebJobs.IWebJobsBuilder builder) { throw null; } + } + public partial class SignalRWebJobsStartup : Microsoft.Azure.WebJobs.Hosting.IWebJobsStartup + { + public SignalRWebJobsStartup() { } + public void Configure(Microsoft.Azure.WebJobs.IWebJobsBuilder builder) { } + } + public static partial class StaticServiceHubContextStore + { + public static Microsoft.Azure.WebJobs.Extensions.SignalRService.IServiceHubContextStore Get(string connectionStringSetting = "AzureSignalRConnectionString") { throw null; } + } +} diff --git a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/api/Microsoft.Azure.WebJobs.Extensions.SignalRService.netstandard2.0.cs b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/api/Microsoft.Azure.WebJobs.Extensions.SignalRService.netstandard2.0.cs index 73cadc31013a3..6dd1355221126 100644 --- a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/api/Microsoft.Azure.WebJobs.Extensions.SignalRService.netstandard2.0.cs +++ b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/api/Microsoft.Azure.WebJobs.Extensions.SignalRService.netstandard2.0.cs @@ -254,8 +254,8 @@ public partial class SignalRParameterAttribute : System.Attribute { public SignalRParameterAttribute() { } } - [Microsoft.Azure.WebJobs.Description.BindingAttribute] - [System.AttributeUsageAttribute(System.AttributeTargets.Parameter | System.AttributeTargets.ReturnValue)] + [Microsoft.Azure.WebJobs.Description.BindingAttribute(TriggerHandlesReturnValue=true)] + [System.AttributeUsageAttribute(System.AttributeTargets.Parameter)] public partial class SignalRTriggerAttribute : System.Attribute { public SignalRTriggerAttribute() { } diff --git a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/Config/SignalRConfigProvider.cs b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/Config/SignalRConfigProvider.cs index eca9deb7ddd96..cf2916cd370cb 100644 --- a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/Config/SignalRConfigProvider.cs +++ b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/Config/SignalRConfigProvider.cs @@ -71,7 +71,7 @@ public void Initialize(ExtensionConfigContext context) // Trigger binding rule var triggerBindingRule = context.AddBindingRule(); triggerBindingRule.AddConverter(JObject.FromObject); - triggerBindingRule.BindToTrigger(new SignalRTriggerBindingProvider(_dispatcher, _nameResolver, _serviceManagerStore, webhookException)); + triggerBindingRule.BindToTrigger(new SignalRTriggerBindingProvider(_dispatcher, _nameResolver, _serviceManagerStore, webhookException)); // Non-trigger binding rule var signalRConnectionInfoAttributeRule = context.AddBindingRule(); diff --git a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/Microsoft.Azure.WebJobs.Extensions.SignalRService.csproj b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/Microsoft.Azure.WebJobs.Extensions.SignalRService.csproj index 7b7c0e1b1eb95..6b4ad6a1e737d 100644 --- a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/Microsoft.Azure.WebJobs.Extensions.SignalRService.csproj +++ b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/Microsoft.Azure.WebJobs.Extensions.SignalRService.csproj @@ -1,11 +1,10 @@ - $(RequiredTargetFrameworks) + $(RequiredTargetFrameworks);net6.0;net8.0 Microsoft.Azure.WebJobs.Extensions.SignalRService - 1.15.0-beta.1 + 1.15.0 - 1.14.0 true true $(NoWarn);CS1591;AZC0107; diff --git a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/Executor/SignalRInvocationMethodExecutor.cs b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/Executor/SignalRInvocationMethodExecutor.cs index 6664ed1818098..2d7bfb5142175 100644 --- a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/Executor/SignalRInvocationMethodExecutor.cs +++ b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/Executor/SignalRInvocationMethodExecutor.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR.Protocol; +using Newtonsoft.Json.Linq; using InvocationMessage = Microsoft.Azure.SignalR.Serverless.Protocols.InvocationMessage; namespace Microsoft.Azure.WebJobs.Extensions.SignalRService @@ -52,7 +53,7 @@ public override async Task ExecuteAsync(HttpRequestMessage else { var result = await tcs.Task.ConfigureAwait(false); - completionMessage = CompletionMessage.WithResult(message.InvocationId, result); + completionMessage = CompletionMessage.WithResult(message.InvocationId, ToSafeSerializationType(result)); response = new HttpResponseMessage(HttpStatusCode.OK); } } @@ -68,6 +69,18 @@ public override async Task ExecuteAsync(HttpRequestMessage return response; } + private static object ToSafeSerializationType(object result) + { + if (result is JToken jtoken) + { + return new JTokenWrapper(jtoken); + } + else + { + return result; + } + } + private static void AssertConsistency(InvocationContext context, InvocationMessage message) { if (!string.Equals(context.Event, message.Target, StringComparison.OrdinalIgnoreCase)) diff --git a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/SignalRTriggerAttribute.cs b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/SignalRTriggerAttribute.cs index 155ac4da99baa..c9803d150e6ff 100644 --- a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/SignalRTriggerAttribute.cs +++ b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/SignalRTriggerAttribute.cs @@ -10,8 +10,10 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService /// /// Attribute used to mark a function that should be triggered by messages sent from SignalR clients. /// - [AttributeUsage(AttributeTargets.ReturnValue | AttributeTargets.Parameter)] - [Binding] + [AttributeUsage(AttributeTargets.Parameter)] +#pragma warning disable CS0618 // Type or member is obsolete + [Binding(TriggerHandlesReturnValue = true)] +#pragma warning restore CS0618 // Type or member is obsolete public class SignalRTriggerAttribute : Attribute { /// diff --git a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/SignalRTriggerBinding.cs b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/SignalRTriggerBinding.cs index 96c01ac430621..78c485d53faf2 100644 --- a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/SignalRTriggerBinding.cs +++ b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/SignalRTriggerBinding.cs @@ -17,6 +17,7 @@ using Microsoft.Azure.WebJobs.Host.Protocols; using Microsoft.Azure.WebJobs.Host.Triggers; using Microsoft.Extensions.Options; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace Microsoft.Azure.WebJobs.Extensions.SignalRService @@ -201,6 +202,11 @@ public Task GetValueAsync() { return Task.FromResult(JObject.FromObject(_value)); } + // Isolated worker model + else if (_parameter.ParameterType == typeof(string)) + { + return Task.FromResult(JsonConvert.SerializeObject(_value) as object); + } return Task.FromResult(null); } @@ -210,7 +216,7 @@ public string ToInvokeString() return _value.ToString(); } - public Type Type => _parameter.GetType(); + public Type Type => _parameter.ParameterType; // No use here public Task SetValueAsync(object value, CancellationToken cancellationToken) diff --git a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/Utils/JTokenWrapper.cs b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/Utils/JTokenWrapper.cs new file mode 100644 index 0000000000000..7f84b762a3c70 --- /dev/null +++ b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/src/TriggerBindings/Utils/JTokenWrapper.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Text.Json; +using Microsoft.AspNetCore.SignalR.Protocol; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Microsoft.Azure.WebJobs.Extensions.SignalRService; + +/// +/// Helps to make the object correctly seralized in that using System.Text.Json internally. +/// Since .Net Core 3.0, the uses System.Text.Json library for JSON (de)serialization, which cannot handle correctly. However, in isolated worker model, if a SignalR trigger function returns an object, then the SignalR extensions in host process gets a object. We need to make sure the object serialized correctly in the . +/// + +[System.Text.Json.Serialization.JsonConverter(typeof(JTokenWrapperJsonConverter))] +internal class JTokenWrapper +{ + public JTokenWrapper(JToken value) + { + Value = value; + } + + public JToken Value { get; } +} + +internal class JTokenWrapperJsonConverter : System.Text.Json.Serialization.JsonConverter +{ + public override JTokenWrapper Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } + + public override void Write(Utf8JsonWriter writer, JTokenWrapper value, JsonSerializerOptions options) + { +#if NET6_0_OR_GREATER + var jsonString = JsonConvert.SerializeObject(value.Value); + writer.WriteRawValue(jsonString); +#elif NETSTANDARD2_0 + // No need to implement. + // First of all, the SignalR extensions for host process always run on .NET 6 or greater runtime when this class is first written. + // Even if somehow the extensions run on .NET Framework, the JsonHubProtocol would use Newtonsoft.Json for serialization and this class would not be used. + throw new NotImplementedException("Serializing Newtonsoft.Json.JsonToken with System.Text.Json is not implemented. "); +#endif + } +} diff --git a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/tests/DefaultSecurityTokenValidatorTests.cs b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/tests/DefaultSecurityTokenValidatorTests.cs index 5eab0f9d18efe..9ccd90fef82a7 100644 --- a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/tests/DefaultSecurityTokenValidatorTests.cs +++ b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/tests/DefaultSecurityTokenValidatorTests.cs @@ -41,7 +41,7 @@ public void ValidateSecurityTokenFacts(string tokenString, SecurityTokenStatus e { var ctx = new DefaultHttpContext(); var req = ctx.Request; - req.Headers.Add("Authorization", new StringValues(tokenString)); + req.Headers.Append("Authorization", new StringValues(tokenString)); Action configureTokenValidationParameters = parameters => { diff --git a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/tests/Utils/TestHelpers.cs b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/tests/Utils/TestHelpers.cs index ee59c657f0adf..bfb73d56fbfac 100644 --- a/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/tests/Utils/TestHelpers.cs +++ b/sdk/signalr/Microsoft.Azure.WebJobs.Extensions.SignalRService/tests/Utils/TestHelpers.cs @@ -68,13 +68,13 @@ public static HttpRequestMessage CreateHttpRequestMessage(string hub, string cat var context = new DefaultHttpContext(); context.Request.ContentType = contentType; context.Request.Method = "Post"; - context.Request.Headers.Add(Constants.AsrsHubNameHeader, hub); - context.Request.Headers.Add(Constants.AsrsCategory, category); - context.Request.Headers.Add(Constants.AsrsEvent, @event); - context.Request.Headers.Add(Constants.AsrsConnectionIdHeader, connectionId); + context.Request.Headers.Append(Constants.AsrsHubNameHeader, hub); + context.Request.Headers.Append(Constants.AsrsCategory, category); + context.Request.Headers.Append(Constants.AsrsEvent, @event); + context.Request.Headers.Append(Constants.AsrsConnectionIdHeader, connectionId); if (signatures != null) { - context.Request.Headers.Add(Constants.AsrsSignature, string.Join(",", signatures)); + context.Request.Headers.Append(Constants.AsrsSignature, string.Join(",", signatures)); } context.Request.Body = content == null ? Stream.Null : new MemoryStream(content);