This commit is contained in:
yzt 2021-03-22 15:56:40 +08:00 коммит произвёл GitHub
Родитель e34b3a382c
Коммит 0d16226e19
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
76 изменённых файлов: 591 добавлений и 595 удалений

Просмотреть файл

@ -22,4 +22,4 @@ namespace Microsoft.Azure.SignalR.Serverless.Protocols
/// </summary>
public const int CloseConnectionMessageType = 11;
}
}
}

Просмотреть файл

@ -1,10 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.Azure.SignalR.Serverless.Protocols
{
@ -24,4 +21,4 @@ namespace Microsoft.Azure.SignalR.Serverless.Protocols
/// <returns>A value that is <c>true</c> if the <see cref="ServerlessMessage"/> was successfully parsed; otherwise, <c>false</c>.</returns>
bool TryParseMessage(ref ReadOnlySequence<byte> input, out ServerlessMessage message);
}
}
}

Просмотреть файл

@ -39,6 +39,7 @@ namespace Microsoft.AspNetCore.Internal
public override bool CanRead => false;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Position
{
get => throw new NotSupportedException();
@ -258,10 +259,16 @@ namespace Microsoft.AspNetCore.Internal
Debug.Assert(_bytesWritten == totalWritten + _position);
}
public override void Flush() { }
public override void Flush()
{
}
public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException();
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotSupportedException();
public override void WriteByte(byte value)
@ -341,4 +348,4 @@ namespace Microsoft.AspNetCore.Internal
}
}
}
}
}

Просмотреть файл

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using MessagePack;
namespace Microsoft.Azure.SignalR.Serverless.Protocols
@ -187,4 +186,4 @@ namespace Microsoft.Azure.SignalR.Serverless.Protocols
}
}
}
}
}

Просмотреть файл

@ -3,9 +3,7 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Microsoft.Azure.SignalR.Serverless.Protocols
{
@ -99,4 +97,4 @@ namespace Microsoft.Azure.SignalR.Serverless.Protocols
}
}
}
}
}

Просмотреть файл

@ -4,7 +4,6 @@
using System;
using System.Buffers;
using System.IO;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@ -57,4 +56,4 @@ namespace Microsoft.Azure.SignalR.Serverless.Protocols
}
}
}
}
}

Просмотреть файл

@ -1,10 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using MessagePack;
@ -49,4 +46,4 @@ namespace Microsoft.Azure.SignalR.Serverless.Protocols
return invocationMessage;
}
}
}
}

Просмотреть файл

@ -32,4 +32,4 @@ namespace Microsoft.Azure.SignalR.Serverless.Protocols
[JsonProperty(PropertyName = "error")]
public string Error { get; set; }
}
}
}

Просмотреть файл

@ -30,7 +30,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
if (request?.Headers.TryGetValue(AuthHeaderName, out var authHeader) == true)
{
var authHeaderValue = authHeader.ToString();
var authHeaderValue = authHeader.ToString();
if (authHeaderValue.StartsWith(BearerPrefix, StringComparison.OrdinalIgnoreCase))
{
var token = authHeaderValue.Substring(BearerPrefix.Length);
@ -59,7 +59,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
// 'nbf' claim is greater than 'exp' claim
ex is SecurityTokenInvalidLifetimeException ||
// Signature is not properly formatted.
// Signature is not properly formatted.
ex is SecurityTokenInvalidSignatureException ||
// 1. 'exp' claim is missing and TokenValidationParameters.RequireExpirationTime is true.

Просмотреть файл

@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Security.Claims;
@ -16,7 +15,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
/// User identity for a SignalR connection
/// </summary>
public string UserId { get; set; }
/// <summary>
/// Custom claims that added to SignalR access token.
/// </summary>

Просмотреть файл

@ -1,7 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Newtonsoft.Json.Linq;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService

Просмотреть файл

@ -9,6 +9,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("accessToken")]
public string AccessToken { get; set; }
}

Просмотреть файл

@ -14,6 +14,7 @@ using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using BindingData = System.Collections.Generic.IReadOnlyDictionary<string, object>;
using BindingDataContract = System.Collections.Generic.IReadOnlyDictionary<string, System.Type>;
// Func to transform Attribute,BindingData into value for cloned attribute property/constructor arg
// Attribute is the new cloned attribute - null if constructor arg (new cloned attr not created yet)
using BindingDataResolver = System.Func<System.Attribute, System.Collections.Generic.IReadOnlyDictionary<string, object>, object>;
@ -21,6 +22,7 @@ using BindingDataResolver = System.Func<System.Attribute, System.Collections.Gen
using Validator = System.Action<object>;
#pragma warning disable CS0618 // Type or member is obsolete
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
// Clone an attribute and resolve it.
@ -98,7 +100,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
// transforms binding data to appropriate resolver (appsetting, autoresolve, or originalValue)
private BindingDataResolver GetResolver(PropertyInfo propInfo, INameResolver nameResolver, BindingDataContract contract)
{
// Do the attribute lookups once upfront, and then cache them (via func closures) for subsequent runtime usage.
// Do the attribute lookups once upfront, and then cache them (via func closures) for subsequent runtime usage.
object originalValue = propInfo.GetValue(_source);
ConnectionStringAttribute connStrAttr = propInfo.GetCustomAttribute<ConnectionStringAttribute>();
AppSettingAttribute appSettingAttr = propInfo.GetCustomAttribute<AppSettingAttribute>();
@ -109,7 +111,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
validator(originalValue);
// No special attributes, treat as literal.
// No special attributes, treat as literal.
return (newAttr, bindingData) => originalValue;
}
@ -119,7 +121,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
throw new InvalidOperationException($"Property '{propInfo.Name}' can only be annotated with one of the types {nameof(AppSettingAttribute)}, {nameof(AutoResolveAttribute)}, and {nameof(ConnectionStringAttribute)}.");
}
// attributes only work on string properties.
// attributes only work on string properties.
if (propInfo.PropertyType != typeof(string))
{
throw new InvalidOperationException($"{nameof(ConnectionStringAttribute)}, {nameof(AutoResolveAttribute)}, or {nameof(AppSettingAttribute)} property '{propInfo.Name}' must be of type string.");
@ -144,7 +146,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
return GetAutoResolveResolver(str, autoResolveAttr, nameResolver, propInfo, contract, validator);
}
// Apply AutoResolve attribute
// Apply AutoResolve attribute
internal BindingDataResolver GetAutoResolveResolver(string originalValue, AutoResolveAttribute autoResolveAttr, INameResolver nameResolver, PropertyInfo propInfo, BindingDataContract contract, Validator validator)
{
if (string.IsNullOrWhiteSpace(originalValue))
@ -187,19 +189,19 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
throw new InvalidOperationException($"Unable to resolve the value for property '{propInfo.DeclaringType.Name}.{propInfo.Name}'. Make sure the setting exists and has a valid value.");
}
// validate after the %% is substituted.
// validate after the %% is substituted.
validator(resolvedValue);
return (newAttr, bindingData) => resolvedValue;
}
// Run validition. This needs to be run at different stages.
// Run validition. This needs to be run at different stages.
// In general, run as early as possible. If there are { } tokens, then we can't run until runtime.
// But if there are no { }, we can run statically.
// But if there are no { }, we can run statically.
// If there's no [AutoResolve], [AppSettings], then we can run immediately.
private static Validator GetValidatorFunc(PropertyInfo propInfo, bool dontLogValues)
{
// This implicitly caches the attribute lookup once and then shares for each runtime invocation.
// This implicitly caches the attribute lookup once and then shares for each runtime invocation.
var attrs = propInfo.GetCustomAttributes<ValidationAttribute>();
return (value) =>
@ -225,8 +227,8 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
};
}
// Resolve for AutoResolve.Default templates.
// These only have access to the {sys} builtin variable and don't get access to trigger binding data.
// Resolve for AutoResolve.Default templates.
// These only have access to the {sys} builtin variable and don't get access to trigger binding data.
internal static BindingDataResolver GetBuiltinTemplateResolver(string originalValue, INameResolver nameResolver, Validator validator)
{
string resolvedValue = nameResolver.ResolveWholeString(originalValue);
@ -234,13 +236,13 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
var template = BindingTemplate.FromString(resolvedValue);
if (!template.HasParameters)
{
// No { } tokens, bind eagerly up front.
// No { } tokens, bind eagerly up front.
validator(originalValue);
}
SystemBindingData.ValidateStaticContract(template);
// For static default contracts, we only have access to the built in binding data.
// For static default contracts, we only have access to the built in binding data.
return (newAttr, bindingData) =>
{
var newValue = template.Bind(SystemBindingData.GetSystemBindingData(bindingData));
@ -257,7 +259,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
if (!template.HasParameters)
{
// No { } tokens, bind eagerly up front.
// No { } tokens, bind eagerly up front.
validator(resolvedValue);
}
@ -334,7 +336,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
if (bindingData == null)
{
// Skip validation if no binding data provided. We can't do the { } substitutions.
// Skip validation if no binding data provided. We can't do the { } substitutions.
return template?.Pattern;
}
@ -363,7 +365,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
}
}
// return the default policy
// return the default policy
return new DefaultResolutionPolicy();
}
@ -377,16 +379,16 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
/// Class providing support for built in system binding expressions
/// </summary>
/// <remarks>
/// It's expected this class is created and added to the binding data.
/// It's expected this class is created and added to the binding data.
/// </remarks>
private class SystemBindingData
{
// The public name for this binding in the binding expressions.
// The public name for this binding in the binding expressions.
public const string Name = "sys";
// An internal name for this binding that uses characters that gaurantee it can't be overwritten by a user.
// This is never seen by the user.
// This ensures that we can always unambiguously retrieve this later.
// An internal name for this binding that uses characters that gaurantee it can't be overwritten by a user.
// This is never seen by the user.
// This ensures that we can always unambiguously retrieve this later.
private const string InternalKeyName = "$sys";
private static readonly IReadOnlyDictionary<string, Type> DefaultSystemContract = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
@ -395,13 +397,13 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
};
/// <summary>
/// The method name that the binding lives in.
/// The method name can be override by the <see cref="FunctionNameAttribute"/>
/// The method name that the binding lives in.
/// The method name can be override by the <see cref="FunctionNameAttribute"/>
/// </summary>
public string MethodName { get; set; }
/// <summary>
/// Get the current UTC date.
/// Get the current UTC date.
/// </summary>
public DateTime UtcNow => DateTime.UtcNow;
@ -411,7 +413,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public Guid RandGuid => Guid.NewGuid();
// Given a full bindingData, create a binding data with just the system object .
// This can be used when resolving default contracts that shouldn't be using an instance binding data.
// This can be used when resolving default contracts that shouldn't be using an instance binding data.
internal static IReadOnlyDictionary<string, object> GetSystemBindingData(IReadOnlyDictionary<string, object> bindingData)
{
var data = GetFromData(bindingData);
@ -422,8 +424,8 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
return systemBindingData;
}
// Validate that a template only uses static (non-instance) binding variables.
// Enforces we're not referring to other data from the trigger.
// Validate that a template only uses static (non-instance) binding variables.
// Enforces we're not referring to other data from the trigger.
internal static void ValidateStaticContract(BindingTemplate template)
{
try
@ -438,12 +440,12 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
internal void AddToBindingData(Dictionary<string, object> bindingData)
{
// User data takes precedence, so if 'sys' already exists, add via the internal name.
// User data takes precedence, so if 'sys' already exists, add via the internal name.
string sysName = bindingData.ContainsKey(SystemBindingData.Name) ? SystemBindingData.InternalKeyName : SystemBindingData.Name;
bindingData[sysName] = this;
}
// Given per-instance binding data, extract just the system binding data object from it.
// Given per-instance binding data, extract just the system binding data object from it.
private static SystemBindingData GetFromData(IReadOnlyDictionary<string, object> bindingData)
{
object val;
@ -459,10 +461,10 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
}
}
// Helpers for providing default behavior for an IAttributeInvokeDescriptor that
// convert between a TAttribute and a string representation (invoke string).
// Properties with [AutoResolve] are the interesting ones to serialize and deserialize.
// Assume any property without a [AutoResolve] attribute is read-only and so doesn't need to be included in the invoke string.
// Helpers for providing default behavior for an IAttributeInvokeDescriptor that
// convert between a TAttribute and a string representation (invoke string).
// Properties with [AutoResolve] are the interesting ones to serialize and deserialize.
// Assume any property without a [AutoResolve] attribute is read-only and so doesn't need to be included in the invoke string.
private static class DefaultAttributeInvokerDescriptor
{
public static TAttribute FromInvokeString(AttributeCloner<TAttribute> cloner, string invokeString)
@ -473,8 +475,8 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
}
// Instantiating new attributes can be tricky since sometimes the arg is to the ctor and sometimes
// its a property setter. AttributeCloner already solves this, so use it here to do the actual attribute instantiation.
// This has an instantiation problem similar to what Attribute Cloner has
// its a property setter. AttributeCloner already solves this, so use it here to do the actual attribute instantiation.
// This has an instantiation problem similar to what Attribute Cloner has
if (invokeString[0] == '{')
{
var propertyValues = JsonConvert.DeserializeObject<IDictionary<string, string>>(invokeString);
@ -516,9 +518,9 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
}
/// <summary>
/// Resolution policy for { } in binding templates.
/// The default policy is just a direct substitution for the binding data.
/// Derived policies can enforce formatting / escaping when they do injection.
/// Resolution policy for { } in binding templates.
/// The default policy is just a direct substitution for the binding data.
/// Derived policies can enforce formatting / escaping when they do injection.
/// </summary>
private class DefaultResolutionPolicy : IResolutionPolicy
{
@ -529,4 +531,5 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
}
}
}
#pragma warning restore CS0618 // Type or member is obsolete
#pragma warning restore CS0618 // Type or member is obsolete

Просмотреть файл

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Protocols;
@ -11,7 +10,7 @@ using Microsoft.Extensions.Configuration;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
// Helper class for implementing IBinding with the attribute resolver pattern.
// Helper class for implementing IBinding with the attribute resolver pattern.
internal abstract class BindingBase<TAttribute> : IBinding
where TAttribute : Attribute
{
@ -51,7 +50,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public Task<IValueProvider> BindAsync(object value, ValueBindingContext context)
{
throw new NotImplementedException();
throw new NotImplementedException();
}
public ParameterDescriptor ToParameterDescriptor()
@ -59,5 +58,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
return param;
}
}
}
}

Просмотреть файл

@ -1,11 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Protocols;
using System;
using System.Threading.Tasks;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
@ -35,7 +35,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
return Task.FromResult<IValueProvider>(new SecurityTokenValidationValueProvider(null, ""));
}
return Task.FromResult<IValueProvider>(new SecurityTokenValidationValueProvider(securityTokenValidator.ValidateToken(request), ""));
return Task.FromResult<IValueProvider>(new SecurityTokenValidationValueProvider(securityTokenValidator.ValidateToken(request), ""));
}
public Task<IValueProvider> BindAsync(BindingContext context)

Просмотреть файл

@ -15,7 +15,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
// todo: fix invoke string in another PR
public SecurityTokenValidationValueProvider(SecurityTokenResult result, string invokeString)
{
this.result= result;
this.result = result;
this.invokeString = invokeString;
}
@ -31,4 +31,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public Type Type => typeof(SecurityTokenResult);
}
}
}

Просмотреть файл

@ -47,4 +47,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
return info;
}
}
}
}

Просмотреть файл

@ -2,9 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
@ -83,4 +81,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
return null;
}
}
}
}

Просмотреть файл

@ -51,10 +51,10 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
var serviceHubContext = await GetHubContextAsync();
var negotiateResponse = await serviceHubContext.NegotiateAsync(new NegotiationOptions()
{
UserId = userId,
Claims = BuildJwtClaims(claims, AzureSignalRUserPrefix).ToList(),
HttpContext = httpContext
{
UserId = userId,
Claims = BuildJwtClaims(claims, AzureSignalRUserPrefix).ToList(),
HttpContext = httpContext
});
return new SignalRConnectionInfo
{

Просмотреть файл

@ -8,13 +8,21 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
internal interface IAzureSignalRSender
{
Task SendToAll(SignalRData data);
Task SendToConnection(string connectionId, SignalRData data);
Task SendToUser(string userId, SignalRData data);
Task SendToGroup(string group, SignalRData data);
Task AddUserToGroup(SignalRGroupAction action);
Task RemoveUserFromGroup(SignalRGroupAction action);
Task RemoveUserFromAllGroups(SignalRGroupAction action);
Task AddConnectionToGroup(SignalRGroupAction action);
Task RemoveConnectionFromGroup(SignalRGroupAction action);
}
}
}

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.Azure.SignalR;
using Microsoft.Azure.SignalR;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{

Просмотреть файл

@ -65,4 +65,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
return configuration.GetReloadToken();
}
}
}
}

Просмотреть файл

@ -27,7 +27,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public ValueTask<IServiceHubContext> GetAsync(string hubName)
{
var pair = store.GetOrAdd(hubName,
var pair = store.GetOrAdd(hubName,
(new Lazy<Task<IServiceHubContext>>(
() => ServiceManager.CreateHubContextAsync(hubName)), default));
return GetAsyncCore(hubName, pair);

Просмотреть файл

@ -1,13 +1,13 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.IdentityModel.Tokens;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
@ -43,13 +43,13 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
throw new NotSupportedException($"{nameof(ISecurityTokenValidator)} already injected.");
}
builder.Services
.AddSingleton<ISecurityTokenValidator>(s =>
new DefaultSecurityTokenValidator(configureTokenValidationParameters));
builder.Services.
TryAddSingleton<ISignalRConnectionInfoConfigurer>(s =>
TryAddSingleton<ISignalRConnectionInfoConfigurer>(s =>
internalSignalRConnectionInfoConfigurer);
return builder;

Просмотреть файл

@ -2,13 +2,12 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
// Then all resolve jobs are put in resolvers, we can also remove the SignalROption after we apply resolve jobs inside bindings.
/// <summary>
/// Extension methods for SignalR Service integration
/// </summary>

Просмотреть файл

@ -14,4 +14,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
internal static IServiceManagerStore ServiceManagerStore { get; set; }
}
}
}

Просмотреть файл

@ -35,4 +35,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public const string Connected = "connected";
public const string Disconnected = "disconnected";
}
}
}

Просмотреть файл

@ -8,4 +8,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public static readonly string EmptyConnectionStringErrorMessageFormat =
$"The SignalR Service connection string or endpoints are not set.";
}
}
}

Просмотреть файл

@ -9,4 +9,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
}
}
}
}

Просмотреть файл

@ -15,4 +15,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
}
}
}
}

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
{
internal class SignalRTriggerParametersNotMatchException : SignalRTriggerException
{
public SignalRTriggerParametersNotMatchException(int excepted, int actual) : base(
@ -10,4 +10,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
}
}
}
}

Просмотреть файл

@ -4,4 +4,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("SignalRServiceExtension.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

Просмотреть файл

@ -1,8 +1,8 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.Azure.WebJobs.Description;
using System;
using Microsoft.Azure.WebJobs.Description;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
@ -11,4 +11,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public class SecurityTokenValidationAttribute : Attribute
{
}
}
}

Просмотреть файл

@ -2,8 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Security.Claims;
using Microsoft.Azure.WebJobs.Description;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
@ -25,4 +23,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public string[] ClaimTypeList { get; set; }
}
}
}

Просмотреть файл

@ -3,9 +3,9 @@
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.Hosting;
[assembly: WebJobsStartup(typeof(SignalRWebJobsStartup))]
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
public class SignalRWebJobsStartup : IWebJobsStartup

Просмотреть файл

@ -1,64 +1,67 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
public class InvocationContext
{
/// <summary>
/// The arguments of invocation message.
/// </summary>
public object[] Arguments { get; set; }
/// <summary>
/// The error message of close connection event.
/// Only close connection message can have this property, and it can be empty if connections close with no error.
/// </summary>
public string Error { get; set; }
/// <summary>
/// The category of the message.
/// </summary>
public string Category { get; set; }
/// <summary>
/// The event of the message.
/// </summary>
public string Event { get; set; }
/// <summary>
/// The hub which message belongs to.
/// </summary>
public string Hub { get; set; }
/// <summary>
/// The connection-id of the client which send the message.
/// </summary>
public string ConnectionId { get; set; }
/// <summary>
/// The user identity of the client which send the message.
/// </summary>
public string UserId { get; set; }
/// <summary>
/// The headers of request.
/// Headers with duplicated key will be joined by comma.
/// </summary>
public IDictionary<string, string> Headers { get; set; }
/// <summary>
/// The query of the request when client connect to the service.
/// Queries with duplicated key will be joined by comma.
/// </summary>
public IDictionary<string, string> Query { get; set; }
/// <summary>
/// The claims of the client.
/// If you multiple claims have the same key, only the first one will be reserved.
/// </summary>
public IDictionary<string, string> Claims { get; set; }
}
}
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
public class InvocationContext
{
/// <summary>
/// The arguments of invocation message.
/// </summary>
public object[] Arguments { get; set; }
/// <summary>
/// The error message of close connection event.
/// Only close connection message can have this property, and it can be empty if connections close with no error.
/// </summary>
public string Error { get; set; }
/// <summary>
/// The category of the message.
/// </summary>
public string Category { get; set; }
/// <summary>
/// The event of the message.
/// </summary>
public string Event { get; set; }
/// <summary>
/// The hub which message belongs to.
/// </summary>
public string Hub { get; set; }
/// <summary>
/// The connection-id of the client which send the message.
/// </summary>
public string ConnectionId { get; set; }
/// <summary>
/// The user identity of the client which send the message.
/// </summary>
public string UserId { get; set; }
/// <summary>
/// The headers of request.
/// Headers with duplicated key will be joined by comma.
/// </summary>
public IDictionary<string, string> Headers { get; set; }
/// <summary>
/// The query of the request when client connect to the service.
/// Queries with duplicated key will be joined by comma.
/// </summary>
public IDictionary<string, string> Query { get; set; }
/// <summary>
/// The claims of the client.
/// If you multiple claims have the same key, only the first one will be reserved.
/// </summary>
public IDictionary<string, string> Claims { get; set; }
}
}

Просмотреть файл

@ -12,4 +12,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public AccessKey[] AccessKeys { get; set; }
}
}
}

Просмотреть файл

@ -1,20 +1,15 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
internal class SignalRConnectMethodExecutor : SignalRMethodExecutor
{
public SignalRConnectMethodExecutor(IRequestResolver resolver, ExecutionContext executionContext): base(resolver, executionContext)
public SignalRConnectMethodExecutor(IRequestResolver resolver, ExecutionContext executionContext) : base(resolver, executionContext)
{
}
@ -34,4 +29,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
}
}

Просмотреть файл

@ -1,22 +1,17 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.SignalR.Serverless.Protocols;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
internal class SignalRDisconnectMethodExecutor: SignalRMethodExecutor
internal class SignalRDisconnectMethodExecutor : SignalRMethodExecutor
{
public SignalRDisconnectMethodExecutor(IRequestResolver resolver, ExecutionContext executionContext): base(resolver, executionContext)
public SignalRDisconnectMethodExecutor(IRequestResolver resolver, ExecutionContext executionContext) : base(resolver, executionContext)
{
}
@ -34,4 +29,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
}
}

Просмотреть файл

@ -2,12 +2,8 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Protocol;
@ -15,9 +11,9 @@ using InvocationMessage = Microsoft.Azure.SignalR.Serverless.Protocols.Invocatio
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
internal class SignalRInvocationMethodExecutor: SignalRMethodExecutor
internal class SignalRInvocationMethodExecutor : SignalRMethodExecutor
{
public SignalRInvocationMethodExecutor(IRequestResolver resolver, ExecutionContext executionContext): base(resolver, executionContext)
public SignalRInvocationMethodExecutor(IRequestResolver resolver, ExecutionContext executionContext) : base(resolver, executionContext)
{
}
@ -48,7 +44,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
if (!functionResult.Succeeded)
{
var errorMessage = functionResult.Exception?.InnerException?.Message ??
functionResult.Exception?.Message ??
functionResult.Exception?.Message ??
"Method execution failed.";
completionMessage = CompletionMessage.WithError(message.InvocationId, errorMessage);
response = new HttpResponseMessage(HttpStatusCode.OK);
@ -81,4 +77,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
}
}
}
}
}

Просмотреть файл

@ -3,7 +3,7 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Executors;
@ -58,4 +58,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
return result;
}
}
}
}

Просмотреть файл

@ -4,7 +4,6 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Executors;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
@ -14,4 +13,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
Task<HttpResponseMessage> ExecuteAsync(HttpRequestMessage req, CancellationToken token = default);
}
}
}

Просмотреть файл

@ -33,4 +33,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
return (await StaticServiceHubContextStore.Get().GetAsync(invocationContext.Hub)).UserGroups;
}
}
}
}

Просмотреть файл

@ -1,14 +1,13 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host.Listeners;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
internal class NullListener: IListener
internal class NullListener : IListener
{
public NullListener()
{
@ -32,4 +31,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
}
}
}
}

Просмотреть файл

@ -36,7 +36,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
return contentType == Constants.JsonContentType || contentType == Constants.MessagePackContentType;
}
// The algorithm is defined in spec: Hex_encoded(HMAC_SHA256(access-key, connection-id))
// The algorithm is defined in spec: Hex_encoded(HMAC_SHA256(access-key, connection-id))
public bool ValidateSignature(HttpRequestMessage request, AccessKey[] accessKeys)
{
if (!_validateSignature)

Просмотреть файл

@ -7,7 +7,6 @@ using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Azure.SignalR.Management;

Просмотреть файл

@ -3,7 +3,6 @@
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Host;
@ -31,5 +30,6 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
/// </summary>
public abstract Task FilterAsync(InvocationContext invocationContext, CancellationToken cancellationToken);
}
#pragma warning restore CS0618 // Type or member is obsolete
}
}

Просмотреть файл

@ -13,4 +13,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public class SignalRIgnoreAttribute : Attribute
{
}
}
}

Просмотреть файл

@ -15,4 +15,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public class SignalRParameterAttribute : Attribute
{
}
}
}

Просмотреть файл

@ -15,7 +15,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
}
public SignalRTriggerAttribute(string hubName, string category, string @event): this(hubName, category, @event, Array.Empty<string>())
public SignalRTriggerAttribute(string hubName, string category, string @event) : this(hubName, category, @event, Array.Empty<string>())
{
}

Просмотреть файл

@ -1,248 +1,251 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.SignalR;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Listeners;
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Listeners;
using Microsoft.Azure.WebJobs.Host.Protocols;
using Microsoft.Azure.WebJobs.Host.Triggers;
using Newtonsoft.Json.Linq;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
internal class SignalRTriggerBinding : ITriggerBinding
{
private const string ReturnParameterKey = "$return";
private readonly ParameterInfo _parameterInfo;
private readonly SignalRTriggerAttribute _attribute;
using Newtonsoft.Json.Linq;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
internal class SignalRTriggerBinding : ITriggerBinding
{
private const string ReturnParameterKey = "$return";
private readonly ParameterInfo _parameterInfo;
private readonly SignalRTriggerAttribute _attribute;
private readonly ISignalRTriggerDispatcher _dispatcher;
private readonly AccessKey[] _accessKeys;
public SignalRTriggerBinding(ParameterInfo parameterInfo, SignalRTriggerAttribute attribute, ISignalRTriggerDispatcher dispatcher, AccessKey[] accessKeys)
{
_parameterInfo = parameterInfo ?? throw new ArgumentNullException(nameof(parameterInfo));
_attribute = attribute ?? throw new ArgumentNullException(nameof(attribute));
private readonly AccessKey[] _accessKeys;
public SignalRTriggerBinding(ParameterInfo parameterInfo, SignalRTriggerAttribute attribute, ISignalRTriggerDispatcher dispatcher, AccessKey[] accessKeys)
{
_parameterInfo = parameterInfo ?? throw new ArgumentNullException(nameof(parameterInfo));
_attribute = attribute ?? throw new ArgumentNullException(nameof(attribute));
_dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
_accessKeys = accessKeys ?? throw new ArgumentNullException(nameof(accessKeys));
BindingDataContract = CreateBindingContract(_attribute, _parameterInfo);
}
public Task<ITriggerData> BindAsync(object value, ValueBindingContext context)
{
var bindingData = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
if (value is SignalRTriggerEvent triggerEvent)
{
var bindingContext = triggerEvent.Context;
// If ParameterNames are set, bind them in order.
// To reduce undefined situation, number of arguments should keep consist with that of ParameterNames
if (_attribute.ParameterNames != null && _attribute.ParameterNames.Length != 0)
{
if (bindingContext.Arguments == null ||
bindingContext.Arguments.Length != _attribute.ParameterNames.Length)
{
throw new SignalRTriggerParametersNotMatchException(_attribute.ParameterNames.Length, bindingContext.Arguments?.Length ?? 0);
}
AddParameterNamesBindingData(bindingData, _attribute.ParameterNames, bindingContext.Arguments);
}
return Task.FromResult<ITriggerData>(new TriggerData(new SignalRTriggerValueProvider(_parameterInfo, bindingContext), bindingData)
{
ReturnValueProvider = triggerEvent.TaskCompletionSource == null ? null : new TriggerReturnValueProvider(triggerEvent.TaskCompletionSource),
});
}
return Task.FromResult<ITriggerData>(null);
}
public Task<IListener> CreateListenerAsync(ListenerFactoryContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// It's not a real listener, and it doesn't need a start or close.
_dispatcher.Map((_attribute.HubName, _attribute.Category, _attribute.Event),
new ExecutionContext { Executor = context.Executor, AccessKeys = _accessKeys });
return Task.FromResult<IListener>(new NullListener());
}
public ParameterDescriptor ToParameterDescriptor()
{
return new ParameterDescriptor
{
Name = _parameterInfo.Name,
};
}
/// <summary>
/// Type of object in BindAsync
/// </summary>
public Type TriggerValueType => typeof(SignalRTriggerEvent);
// TODO: Use dynamic contract to deal with parameterName
public IReadOnlyDictionary<string, Type> BindingDataContract { get; }
/// <summary>
/// Defined what other bindings can use and return value.
/// </summary>
private IReadOnlyDictionary<string, Type> CreateBindingContract(SignalRTriggerAttribute attribute, ParameterInfo parameter)
{
var contract = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
{
{ ReturnParameterKey, typeof(object).MakeByRefType() },
};
// Add names in ParameterNames to binding contract, that user can bind to Functions' parameter directly
if (attribute.ParameterNames != null)
{
var parameters = ((MethodInfo)parameter.Member).GetParameters().ToDictionary(p => p.Name, p => p.ParameterType, StringComparer.OrdinalIgnoreCase);
foreach (var parameterName in attribute.ParameterNames)
{
if (parameters.ContainsKey(parameterName))
{
contract.Add(parameterName, parameters[parameterName]);
}
else
{
contract.Add(parameterName, typeof(object));
}
}
}
return contract;
}
private void AddParameterNamesBindingData(Dictionary<string, object> bindingData, string[] parameterNames, object[] arguments)
{
var length = parameterNames.Length;
for (var i = 0; i < length; i++)
{
if (BindingDataContract.TryGetValue(parameterNames[i], out var type))
{
bindingData.Add(parameterNames[i], ConvertValueIfNecessary(arguments[i], type));
}
else
{
bindingData.Add(parameterNames[i], arguments[i]);
}
}
}
private object ConvertValueIfNecessary(object value, Type targetType)
{
if (value != null && !targetType.IsAssignableFrom(value.GetType()))
{
var underlyingTargetType = Nullable.GetUnderlyingType(targetType) ?? targetType;
var jObject = value as JObject;
if (jObject != null)
{
value = jObject.ToObject(targetType);
}
else if (underlyingTargetType == typeof(Guid) && value.GetType() == typeof(string))
{
// Guids need to be converted by their own logic
// Intentionally throw here on error to standardize behavior
value = Guid.Parse((string)value);
}
else
{
// if the type is nullable, we only need to convert to the
// correct underlying type
value = Convert.ChangeType(value, underlyingTargetType);
}
}
return value;
}
// TODO: Add more supported type
/// <summary>
/// A provider that responsible for providing value in various type to be bond to function method parameter.
/// </summary>
private class SignalRTriggerValueProvider : IValueBinder
{
private readonly InvocationContext _value;
private readonly ParameterInfo _parameter;
public SignalRTriggerValueProvider(ParameterInfo parameter, InvocationContext value)
{
_parameter = parameter ?? throw new ArgumentNullException(nameof(parameter));
_value = value ?? throw new ArgumentNullException(nameof(value));
}
public Task<object> GetValueAsync()
{
if (_parameter.ParameterType == typeof(InvocationContext))
{
return Task.FromResult<object>(_value);
}
else if (_parameter.ParameterType == typeof(object) ||
_parameter.ParameterType == typeof(JObject))
{
return Task.FromResult<object>(JObject.FromObject(_value));
}
return Task.FromResult<object>(null);
}
public string ToInvokeString()
{
return _value.ToString();
}
public Type Type => _parameter.GetType();
// No use here
public Task SetValueAsync(object value, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
/// <summary>
/// A provider to handle return value.
/// </summary>
private class TriggerReturnValueProvider : IValueBinder
{
private readonly TaskCompletionSource<object> _tcs;
public TriggerReturnValueProvider(TaskCompletionSource<object> tcs)
{
_tcs = tcs;
}
public Task<object> GetValueAsync()
{
// Useless for return value provider
return null;
}
public string ToInvokeString()
{
// Useless for return value provider
return string.Empty;
}
public Type Type => typeof(object).MakeByRefType();
public Task SetValueAsync(object value, CancellationToken cancellationToken)
{
_tcs.TrySetResult(value);
return Task.CompletedTask;
}
}
}
}
BindingDataContract = CreateBindingContract(_attribute, _parameterInfo);
}
public Task<ITriggerData> BindAsync(object value, ValueBindingContext context)
{
var bindingData = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
if (value is SignalRTriggerEvent triggerEvent)
{
var bindingContext = triggerEvent.Context;
// If ParameterNames are set, bind them in order.
// To reduce undefined situation, number of arguments should keep consist with that of ParameterNames
if (_attribute.ParameterNames != null && _attribute.ParameterNames.Length != 0)
{
if (bindingContext.Arguments == null ||
bindingContext.Arguments.Length != _attribute.ParameterNames.Length)
{
throw new SignalRTriggerParametersNotMatchException(_attribute.ParameterNames.Length, bindingContext.Arguments?.Length ?? 0);
}
AddParameterNamesBindingData(bindingData, _attribute.ParameterNames, bindingContext.Arguments);
}
return Task.FromResult<ITriggerData>(new TriggerData(new SignalRTriggerValueProvider(_parameterInfo, bindingContext), bindingData)
{
ReturnValueProvider = triggerEvent.TaskCompletionSource == null ? null : new TriggerReturnValueProvider(triggerEvent.TaskCompletionSource),
});
}
return Task.FromResult<ITriggerData>(null);
}
public Task<IListener> CreateListenerAsync(ListenerFactoryContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
// It's not a real listener, and it doesn't need a start or close.
_dispatcher.Map((_attribute.HubName, _attribute.Category, _attribute.Event),
new ExecutionContext { Executor = context.Executor, AccessKeys = _accessKeys });
return Task.FromResult<IListener>(new NullListener());
}
public ParameterDescriptor ToParameterDescriptor()
{
return new ParameterDescriptor
{
Name = _parameterInfo.Name,
};
}
/// <summary>
/// Type of object in BindAsync
/// </summary>
public Type TriggerValueType => typeof(SignalRTriggerEvent);
// TODO: Use dynamic contract to deal with parameterName
public IReadOnlyDictionary<string, Type> BindingDataContract { get; }
/// <summary>
/// Defined what other bindings can use and return value.
/// </summary>
private IReadOnlyDictionary<string, Type> CreateBindingContract(SignalRTriggerAttribute attribute, ParameterInfo parameter)
{
var contract = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
{
{ ReturnParameterKey, typeof(object).MakeByRefType() },
};
// Add names in ParameterNames to binding contract, that user can bind to Functions' parameter directly
if (attribute.ParameterNames != null)
{
var parameters = ((MethodInfo)parameter.Member).GetParameters().ToDictionary(p => p.Name, p => p.ParameterType, StringComparer.OrdinalIgnoreCase);
foreach (var parameterName in attribute.ParameterNames)
{
if (parameters.ContainsKey(parameterName))
{
contract.Add(parameterName, parameters[parameterName]);
}
else
{
contract.Add(parameterName, typeof(object));
}
}
}
return contract;
}
private void AddParameterNamesBindingData(Dictionary<string, object> bindingData, string[] parameterNames, object[] arguments)
{
var length = parameterNames.Length;
for (var i = 0; i < length; i++)
{
if (BindingDataContract.TryGetValue(parameterNames[i], out var type))
{
bindingData.Add(parameterNames[i], ConvertValueIfNecessary(arguments[i], type));
}
else
{
bindingData.Add(parameterNames[i], arguments[i]);
}
}
}
private object ConvertValueIfNecessary(object value, Type targetType)
{
if (value != null && !targetType.IsAssignableFrom(value.GetType()))
{
var underlyingTargetType = Nullable.GetUnderlyingType(targetType) ?? targetType;
var jObject = value as JObject;
if (jObject != null)
{
value = jObject.ToObject(targetType);
}
else if (underlyingTargetType == typeof(Guid) && value.GetType() == typeof(string))
{
// Guids need to be converted by their own logic
// Intentionally throw here on error to standardize behavior
value = Guid.Parse((string)value);
}
else
{
// if the type is nullable, we only need to convert to the
// correct underlying type
value = Convert.ChangeType(value, underlyingTargetType);
}
}
return value;
}
// TODO: Add more supported type
/// <summary>
/// A provider that responsible for providing value in various type to be bond to function method parameter.
/// </summary>
private class SignalRTriggerValueProvider : IValueBinder
{
private readonly InvocationContext _value;
private readonly ParameterInfo _parameter;
public SignalRTriggerValueProvider(ParameterInfo parameter, InvocationContext value)
{
_parameter = parameter ?? throw new ArgumentNullException(nameof(parameter));
_value = value ?? throw new ArgumentNullException(nameof(value));
}
public Task<object> GetValueAsync()
{
if (_parameter.ParameterType == typeof(InvocationContext))
{
return Task.FromResult<object>(_value);
}
else if (_parameter.ParameterType == typeof(object) ||
_parameter.ParameterType == typeof(JObject))
{
return Task.FromResult<object>(JObject.FromObject(_value));
}
return Task.FromResult<object>(null);
}
public string ToInvokeString()
{
return _value.ToString();
}
public Type Type => _parameter.GetType();
// No use here
public Task SetValueAsync(object value, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
/// <summary>
/// A provider to handle return value.
/// </summary>
private class TriggerReturnValueProvider : IValueBinder
{
private readonly TaskCompletionSource<object> _tcs;
public TriggerReturnValueProvider(TaskCompletionSource<object> tcs)
{
_tcs = tcs;
}
public Task<object> GetValueAsync()
{
// Useless for return value provider
return null;
}
public string ToInvokeString()
{
// Useless for return value provider
return string.Empty;
}
public Type Type => typeof(object).MakeByRefType();
public Task SetValueAsync(object value, CancellationToken cancellationToken)
{
_tcs.TrySetResult(value);
return Task.CompletedTask;
}
}
}
}

Просмотреть файл

@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@ -16,6 +15,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
private readonly Dictionary<(string hub, string category, string @event), SignalRMethodExecutor> _executors =
new Dictionary<(string, string, string), SignalRMethodExecutor>(TupleStringIgnoreCasesComparer.Instance);
private readonly IRequestResolver _resolver;
public SignalRTriggerDispatcher(IRequestResolver resolver = null)
@ -27,7 +27,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
if (!_executors.ContainsKey(key))
{
if (string.Equals(key.category,Category.Connections, StringComparison.OrdinalIgnoreCase))
if (string.Equals(key.category, Category.Connections, StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(key.@event, Event.Connected, StringComparison.OrdinalIgnoreCase))
{
@ -65,7 +65,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
if (_executors.TryGetValue(key, out var executor))
{
try
@ -103,4 +103,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
!string.IsNullOrEmpty(key.@event);
}
}
}
}

Просмотреть файл

@ -17,4 +17,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
/// </summary>
public TaskCompletionSource<object> TaskCompletionSource { get; set; }
}
}
}

Просмотреть файл

@ -17,4 +17,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public override IHubProtocol Protocol { get; } = new JsonHubProtocol();
}
}
}

Просмотреть файл

@ -17,4 +17,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public override IHubProtocol Protocol { get; } = new MessagePackHubProtocol();
}
}
}

Просмотреть файл

@ -29,4 +29,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
public abstract IHubProtocol Protocol { get; }
}
}
}

Просмотреть файл

@ -1,65 +1,62 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
internal static class SignalRTriggerUtils
{
private const string CommaSeparator = ",";
private static readonly char[] HeaderSeparator = { ',' };
private static readonly string[] ClaimsSeparator = { ": " };
public static IDictionary<string, string> GetQueryDictionary(string queryString)
{
if (string.IsNullOrEmpty(queryString))
{
return default;
}
// The query string looks like "?key1=value1&key2=value2"
var queries = QueryHelpers.ParseQuery(queryString);
return queries.ToDictionary(x => x.Key, x => x.Value.ToString());
}
public static IDictionary<string, string> GetClaimDictionary(string claims)
{
if (string.IsNullOrEmpty(claims))
{
return default;
}
// The claim string looks like "a: v, b: v"
return claims.Split(HeaderSeparator, StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Split(ClaimsSeparator, StringSplitOptions.RemoveEmptyEntries)).Where(l => l.Length == 2)
.GroupBy(s => s[0].Trim(), (k, g) => new KeyValuePair<string, string>(k, g.Select(gk => gk[1].Trim()).FirstOrDefault()))
.ToDictionary(x => x.Key, x => x.Value);
}
public static IReadOnlyList<string> GetSignatureList(string signatures)
{
if (string.IsNullOrEmpty(signatures))
{
return default;
}
return signatures.Split(HeaderSeparator, StringSplitOptions.RemoveEmptyEntries);
}
public static IDictionary<string, string> GetHeaderDictionary(HttpRequestHeaders headers)
{
return headers.ToDictionary(x => x.Key, x => string.Join(CommaSeparator, x.Value.ToArray()), StringComparer.OrdinalIgnoreCase);
}
}
}
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using Microsoft.AspNetCore.WebUtilities;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
internal static class SignalRTriggerUtils
{
private const string CommaSeparator = ",";
private static readonly char[] HeaderSeparator = { ',' };
private static readonly string[] ClaimsSeparator = { ": " };
public static IDictionary<string, string> GetQueryDictionary(string queryString)
{
if (string.IsNullOrEmpty(queryString))
{
return default;
}
// The query string looks like "?key1=value1&key2=value2"
var queries = QueryHelpers.ParseQuery(queryString);
return queries.ToDictionary(x => x.Key, x => x.Value.ToString());
}
public static IDictionary<string, string> GetClaimDictionary(string claims)
{
if (string.IsNullOrEmpty(claims))
{
return default;
}
// The claim string looks like "a: v, b: v"
return claims.Split(HeaderSeparator, StringSplitOptions.RemoveEmptyEntries)
.Select(p => p.Split(ClaimsSeparator, StringSplitOptions.RemoveEmptyEntries)).Where(l => l.Length == 2)
.GroupBy(s => s[0].Trim(), (k, g) => new KeyValuePair<string, string>(k, g.Select(gk => gk[1].Trim()).FirstOrDefault()))
.ToDictionary(x => x.Key, x => x.Value);
}
public static IReadOnlyList<string> GetSignatureList(string signatures)
{
if (string.IsNullOrEmpty(signatures))
{
return default;
}
return signatures.Split(HeaderSeparator, StringSplitOptions.RemoveEmptyEntries);
}
public static IDictionary<string, string> GetHeaderDictionary(HttpRequestHeaders headers)
{
return headers.ToDictionary(x => x.Key, x => string.Join(CommaSeparator, x.Value.ToArray()), StringComparer.OrdinalIgnoreCase);
}
}
}

Просмотреть файл

@ -2,13 +2,11 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
internal class TupleStringIgnoreCasesComparer: IEqualityComparer<(string, string, string)>
internal class TupleStringIgnoreCasesComparer : IEqualityComparer<(string, string, string)>
{
public static readonly TupleStringIgnoreCasesComparer Instance = new TupleStringIgnoreCasesComparer();
@ -26,4 +24,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
StringComparer.InvariantCultureIgnoreCase.GetHashCode(obj.Item3);
}
}
}
}

Просмотреть файл

@ -1,4 +1,7 @@
using System;
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Text;
@ -13,7 +16,7 @@ namespace Microsoft.Azure.SignalR.Serverless.Protocols.Tests
{
public static IEnumerable<object[]> GetParameters()
{
var protocols = new string[] {"json", "messagepack"};
var protocols = new string[] { "json", "messagepack" };
foreach (var protocol in protocols)
{
yield return new object[] { protocol, null, Guid.NewGuid().ToString(), new object[0] };
@ -46,7 +49,6 @@ namespace Microsoft.Azure.SignalR.Serverless.Protocols.Tests
new object[] {new object[] { null, Guid.NewGuid().ToString() }}
};
}
}
[Theory]
@ -67,7 +69,7 @@ namespace Microsoft.Azure.SignalR.Serverless.Protocols.Tests
}
var serverlessProtocol = protocolName == "json" ? (IServerlessProtocol)new JsonServerlessProtocol() : new MessagePackServerlessProtocol();
Assert.True(serverlessProtocol.TryParseMessage(ref payload, out var parsedMessage));
var invocationMessage = (InvocationMessage) parsedMessage;
var invocationMessage = (InvocationMessage)parsedMessage;
Assert.Equal(1, invocationMessage.Type);
Assert.Equal(invocationId, invocationMessage.InvocationId);
Assert.Equal(target, invocationMessage.Target);
@ -91,11 +93,11 @@ namespace Microsoft.Azure.SignalR.Serverless.Protocols.Tests
[InlineData(null)]
public void CloseConnectionMessageParseTest(string error)
{
var openConnectionPayload = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new CloseConnectionMessage() { Type = 11, Error = error})));
var openConnectionPayload = new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new CloseConnectionMessage() { Type = 11, Error = error })));
var serverlessProtocol = new JsonServerlessProtocol();
Assert.True(serverlessProtocol.TryParseMessage(ref openConnectionPayload, out var message));
Assert.Equal(error, ((CloseConnectionMessage)message).Error);
Assert.Equal(typeof(CloseConnectionMessage), message.GetType());
}
}
}
}

Просмотреть файл

@ -1,7 +1,8 @@
using System;
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService.Tests.Common
{
@ -82,4 +83,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService.Tests.Common
return lengthPrefixBuffer.ToArray();
}
}
}
}

Просмотреть файл

@ -1,7 +1,7 @@
using System;
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Buffers;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.Azure.WebJobs.Extensions.SignalRService.Tests.Common
{
@ -29,4 +29,4 @@ namespace Microsoft.Azure.WebJobs.Extensions.SignalRService.Tests.Common
return true;
}
}
}
}

Просмотреть файл

@ -29,7 +29,7 @@ namespace Microsoft.Azure.Webjobs.Extensions.SignalRService.E2ETests
return HttpClient.PostAsync($"{url}/api/{SendPath}", content);
}
public static Task Group(string url,SignalRGroupAction action)
public static Task Group(string url, SignalRGroupAction action)
{
const string GroupPath = "group";
var content = JsonContent.Create(action);

Просмотреть файл

@ -21,6 +21,7 @@ namespace Microsoft.Azure.Webjobs.Extensions.SignalRService.E2ETests
{
private const string Section = "SimpleChat";
public static readonly SimpleChatClient Client = new();
public class BaseUrls : IEnumerable<object[]>
{
public static readonly IEnumerable<object[]> Data = from section in UrlConfiguration.GetSection(Section).GetChildren()

Просмотреть файл

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Microsoft.Extensions.Primitives;
@ -31,7 +30,6 @@ namespace SignalRServiceExtension.Tests
"",
SecurityTokenStatus.Empty
}
};
[Theory]
@ -59,4 +57,4 @@ namespace SignalRServiceExtension.Tests
Assert.Equal(expectedStatus, securityTokenResult.Status);
}
}
}
}

Просмотреть файл

@ -1,9 +1,9 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Newtonsoft.Json.Linq;
using System;
using Xunit;
namespace SignalRServiceExtension.Tests

Просмотреть файл

@ -1,6 +1,8 @@
using System;
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -30,28 +32,27 @@ namespace SignalRServiceExtension.Tests.Trigger
.Returns<TriggeredFunctionData, CancellationToken>((data, token) =>
{
_triggeredFunctionDataTcs.TrySetResult(data);
((SignalRTriggerEvent) data.TriggerValue).TaskCompletionSource?.TrySetResult(string.Empty);
((SignalRTriggerEvent)data.TriggerValue).TaskCompletionSource?.TrySetResult(string.Empty);
return Task.FromResult(new FunctionResult(true));
});
_triggeredFunctionExecutor = executorMoc.Object;
}
[Fact]
public async Task SignalRConnectMethodExecutorTest()
{
var resolver = new SignalRRequestResolver(false);
var methodExecutor = new SignalRConnectMethodExecutor(resolver, new ExecutionContext {Executor = _triggeredFunctionExecutor });
var methodExecutor = new SignalRConnectMethodExecutor(resolver, new ExecutionContext { Executor = _triggeredFunctionExecutor });
var hub = Guid.NewGuid().ToString();
var category = Guid.NewGuid().ToString();
var @event = Guid.NewGuid().ToString();
var connectionId = Guid.NewGuid().ToString();
var content = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new OpenConnectionMessage {Type = 10}));
var content = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new OpenConnectionMessage { Type = 10 }));
var request = TestHelpers.CreateHttpRequestMessage(hub, category, @event, connectionId, contentType: Constants.JsonContentType, content: content);
await methodExecutor.ExecuteAsync(request);
var result = await _triggeredFunctionDataTcs.Task;
var triggerData = (SignalRTriggerEvent) result.TriggerValue;
var triggerData = (SignalRTriggerEvent)result.TriggerValue;
Assert.Null(triggerData.TaskCompletionSource);
Assert.Equal(hub, triggerData.Context.Hub);
Assert.Equal(category, triggerData.Context.Category);
@ -96,7 +97,7 @@ namespace SignalRServiceExtension.Tests.Trigger
var category = Guid.NewGuid().ToString();
var @event = Guid.NewGuid().ToString();
var connectionId = Guid.NewGuid().ToString();
var arguments = new object[] {Guid.NewGuid().ToString(), Guid.NewGuid().ToString()};
var arguments = new object[] { Guid.NewGuid().ToString(), Guid.NewGuid().ToString() };
var message = new Microsoft.AspNetCore.SignalR.Protocol.InvocationMessage(Guid.NewGuid().ToString(), @event, arguments);
IHubProtocol protocol = protocolName == "json" ? (IHubProtocol)new JsonHubProtocol() : new MessagePackHubProtocol();
@ -126,4 +127,4 @@ namespace SignalRServiceExtension.Tests.Trigger
Assert.Equal(arguments, triggerData.Context.Arguments);
}
}
}
}

Просмотреть файл

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Reflection;
using System.Threading;
using Microsoft.Azure.WebJobs;
@ -25,7 +27,7 @@ namespace SignalRServiceExtension.Tests
Assert.Equal(nameof(TestServerlessHub), resolvedAttribute.HubName);
Assert.Equal(Category.Messages, resolvedAttribute.Category);
Assert.Equal(nameof(TestServerlessHub.TestFunction), resolvedAttribute.Event);
Assert.Equal(new string[] {"arg0", "arg1"}, resolvedAttribute.ParameterNames);
Assert.Equal(new string[] { "arg0", "arg1" }, resolvedAttribute.ParameterNames);
// With SignalRIgoreAttribute
parameter = typeof(TestServerlessHub).GetMethod(nameof(TestServerlessHub.TestFunctionWithIgnore), BindingFlags.Instance | BindingFlags.NonPublic).GetParameters()[0];
@ -91,7 +93,7 @@ namespace SignalRServiceExtension.Tests
public void ResolveAttributeParameterConflictTest()
{
var bindingProvider = CreateBindingProvider();
var attribute = new SignalRTriggerAttribute(string.Empty, string.Empty, String.Empty, new string[] {"arg0"});
var attribute = new SignalRTriggerAttribute(string.Empty, string.Empty, String.Empty, new string[] { "arg0" });
var parameter = typeof(TestServerlessHub).GetMethod(nameof(TestServerlessHub.TestFunction), BindingFlags.Instance | BindingFlags.NonPublic).GetParameters()[0];
Assert.ThrowsAny<Exception>(() => bindingProvider.GetParameterResolvedAttribute(attribute, parameter));
}
@ -108,7 +110,7 @@ namespace SignalRServiceExtension.Tests
private SignalRTriggerBindingProvider CreateBindingProvider(Exception exception = null)
{
var configuration = new ConfigurationBuilder().AddInMemoryCollection().Build();
configuration[Constants.AzureSignalRConnectionStringName]= "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Version=1.0;";
configuration[Constants.AzureSignalRConnectionStringName] = "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Version=1.0;";
configuration["Serverless_ExpressionBindings_HubName"] = "test_hub";
configuration["Serverless_ExpressionBindings_HubCategory"] = "connections";
configuration["Serverless_ExpressionBindings_HubEvent"] = "connected";
@ -118,37 +120,37 @@ namespace SignalRServiceExtension.Tests
public class TestServerlessHub : ServerlessHub
{
internal void TestFunction([SignalRTrigger]InvocationContext context, string arg0, int arg1)
internal void TestFunction([SignalRTrigger] InvocationContext context, string arg0, int arg1)
{
}
internal void TestFunctionWithIgnore([SignalRTrigger]InvocationContext context, string arg0, int arg1, [SignalRIgnore]int arg2)
internal void TestFunctionWithIgnore([SignalRTrigger] InvocationContext context, string arg0, int arg1, [SignalRIgnore] int arg2)
{
}
internal void TestFunctionWithSpecificType([SignalRTrigger]InvocationContext context, string arg0, int arg1, ILogger logger, CancellationToken token)
internal void TestFunctionWithSpecificType([SignalRTrigger] InvocationContext context, string arg0, int arg1, ILogger logger, CancellationToken token)
{
}
}
public class TestNonServerlessHub
{
internal void TestFunction([SignalRTrigger]InvocationContext context,
[SignalRParameter]string arg0,
[SignalRParameter]int arg1)
internal void TestFunction([SignalRTrigger] InvocationContext context,
[SignalRParameter] string arg0,
[SignalRParameter] int arg1)
{
}
}
public class TestConnectedServerlessHub : ServerlessHub
{
internal void OnConnected([SignalRTrigger]InvocationContext context, string arg0, int arg1)
internal void OnConnected([SignalRTrigger] InvocationContext context, string arg0, int arg1)
{
}
internal void OnDisconnected([SignalRTrigger]InvocationContext context, string arg0, int arg1)
internal void OnDisconnected([SignalRTrigger] InvocationContext context, string arg0, int arg1)
{
}
}
}
}
}

Просмотреть файл

@ -1,4 +1,7 @@
using System;
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
@ -41,11 +44,11 @@ namespace SignalRServiceExtension.Tests
var executor = executorMoc.Object;
if (throwException)
{
Assert.ThrowsAny<Exception>(() => dispatcher.Map(key, new ExecutionContext {Executor = executor, AccessKeys = null}));
Assert.ThrowsAny<Exception>(() => dispatcher.Map(key, new ExecutionContext { Executor = executor, AccessKeys = null }));
return;
}
dispatcher.Map(key, new ExecutionContext {Executor = executor, AccessKeys = null});
dispatcher.Map(key, new ExecutionContext { Executor = executor, AccessKeys = null });
var request = TestHelpers.CreateHttpRequestMessage(key.hub, key.category, key.@event, Guid.NewGuid().ToString());
await dispatcher.ExecuteAsync(request);
executorMoc.Verify(e => e.TryExecuteAsync(It.IsAny<TriggeredFunctionData>(), It.IsAny<CancellationToken>()), Times.Once);
@ -73,7 +76,7 @@ namespace SignalRServiceExtension.Tests
.Returns(Task.FromResult(new FunctionResult(true)));
var executor = executorMoc.Object;
dispatcher.Map(key, new ExecutionContext { Executor = executor, AccessKeys = null });
// Test content type
resolver.ValidateContentTypeResult = false;
var request = TestHelpers.CreateHttpRequestMessage(key.hub, key.category, key.@event, Guid.NewGuid().ToString());
@ -120,4 +123,4 @@ namespace SignalRServiceExtension.Tests
}
}
}
}
}

Просмотреть файл

@ -1,7 +1,9 @@
using System;
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using Microsoft.Azure.SignalR;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using SignalRServiceExtension.Tests.Utils;
@ -39,7 +41,6 @@ namespace SignalRServiceExtension.Tests.Trigger
yield return new object[] { req, accessKeys, false };
}
[Theory]
[MemberData(nameof(SignatureTestData))]
internal void SignatureTest(HttpRequestMessage request, AccessKey[] accessKeys, bool validate)
@ -48,4 +49,4 @@ namespace SignalRServiceExtension.Tests.Trigger
Assert.Equal(validate, resolver.ValidateSignature(request, accessKeys));
}
}
}
}

Просмотреть файл

@ -20,13 +20,14 @@ namespace SignalRServiceExtension.Tests
{
private const string ConnectionString = "Endpoint=http://localhost;AccessKey=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;Version=1.0;";
private static readonly AccessKey[] AccessKeys = new AccessKey[] { new ServiceEndpoint(ConnectionString).AccessKey };
[Fact]
public async Task BindAsyncTest()
{
var binding = CreateBinding(nameof(TestFunction), new string[0]);
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
var context = new InvocationContext();
var triggerContext = new SignalRTriggerEvent {Context = context, TaskCompletionSource = tcs};
var triggerContext = new SignalRTriggerEvent { Context = context, TaskCompletionSource = tcs };
var result = await binding.BindAsync(triggerContext, null);
Assert.Equal(context, await result.ValueProvider.GetValueAsync());
}
@ -52,7 +53,7 @@ namespace SignalRServiceExtension.Tests
public async Task BindingDataTestWithLessParameterNames()
{
var binding = CreateBinding(nameof(TestFunctionWithTwoStringArgument), "arg0");
var context = new InvocationContext{Arguments = new object[] {Guid.NewGuid().ToString()}};
var context = new InvocationContext { Arguments = new object[] { Guid.NewGuid().ToString() } };
var triggerContext = new SignalRTriggerEvent { Context = context };
var result = await binding.BindAsync(triggerContext, null);
var bindingData = result.BindingData;
@ -116,4 +117,4 @@ namespace SignalRServiceExtension.Tests
{
}
}
}
}

Просмотреть файл

@ -1,7 +1,9 @@
using System.Collections.Generic;
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Newtonsoft.Json;
@ -13,17 +15,17 @@ namespace SignalRServiceExtension.Tests
{
public static IEnumerable<object[]> QueryStringTestData()
{
yield return new object[] {"?k=v", new Dictionary<string, string> {["k"] = "v"}};
yield return new object[] { "?k=v", new Dictionary<string, string> { ["k"] = "v" } };
yield return new object[] { "?k1=v1&k2=v2", new Dictionary<string, string> { ["k1"] = "v1", ["k2"] = "v2" } };
yield return new object[] { "?k1=v1&k1=v2", new Dictionary<string, string> { ["k1"] = "v1,v2" }};
yield return new object[] { "?k1=v1&k1=v2&k2=v3", new Dictionary<string, string> { ["k1"] = "v1,v2", ["k2"] = "v3"} };
yield return new object[] { "?k1=v1&k1=v2", new Dictionary<string, string> { ["k1"] = "v1,v2" } };
yield return new object[] { "?k1=v1&k1=v2&k2=v3", new Dictionary<string, string> { ["k1"] = "v1,v2", ["k2"] = "v3" } };
}
public static IEnumerable<object[]> HeaderTestData()
{
var request1 = new HttpRequestMessage();
request1.Headers.Add("k", "v");
yield return new object[] {request1.Headers, new Dictionary<string, string> {["k"] = "v"}};
yield return new object[] { request1.Headers, new Dictionary<string, string> { ["k"] = "v" } };
var request2 = new HttpRequestMessage();
request2.Headers.Add("k1", "v1");
@ -46,7 +48,7 @@ namespace SignalRServiceExtension.Tests
{
yield return new object[] { "k: v", new Dictionary<string, string> { ["k"] = "v" } };
yield return new object[] { "k1: v1, k2: v2", new Dictionary<string, string> { ["k1"] = "v1", ["k2"] = "v2" } };
yield return new object[] { "k1: v1, k1: v2", new Dictionary<string, string> { ["k1"] = "v1" }};
yield return new object[] { "k1: v1, k1: v2", new Dictionary<string, string> { ["k1"] = "v1" } };
yield return new object[] { "k1: v1, k1: v2, k2: v3", new Dictionary<string, string> { ["k1"] = "v1", ["k2"] = "v3" } };
}
@ -74,4 +76,4 @@ namespace SignalRServiceExtension.Tests
Assert.Equal(JsonConvert.SerializeObject(expectedResult), JsonConvert.SerializeObject(result));
}
}
}
}

Просмотреть файл

@ -34,8 +34,9 @@ namespace SignalRServiceExtension.Tests.Utils.Loggings
private class NoopDisposable : IDisposable
{
public static NoopDisposable Instance = new NoopDisposable();
public void Dispose()
{ }
}
}
}
}

Просмотреть файл

@ -21,4 +21,4 @@ namespace SignalRServiceExtension.Tests.Utils.Loggings
public void Dispose()
{ }
}
}
}

Просмотреть файл

@ -28,4 +28,4 @@ namespace SignalRServiceExtension.Tests.Utils
BindToInput<string>(attr => attr.ToBeAutoResolve);
}
}
}
}

Просмотреть файл

@ -60,7 +60,7 @@ namespace SignalRServiceExtension.Tests.Utils
return host.Services.GetService<IJobHost>() as JobHost;
}
public static HttpRequestMessage CreateHttpRequestMessage(string hub, string category, string @event, string connectionId,
public static HttpRequestMessage CreateHttpRequestMessage(string hub, string category, string @event, string connectionId,
string contentType = Constants.JsonContentType, byte[] content = null, string[] signatures = null)
{
var context = new DefaultHttpContext();

Просмотреть файл

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -13,7 +12,7 @@ using ExecutionContext = Microsoft.Azure.WebJobs.Extensions.SignalRService.Execu
namespace SignalRServiceExtension.Tests.Utils
{
class TestTriggerDispatcher : ISignalRTriggerDispatcher
internal class TestTriggerDispatcher : ISignalRTriggerDispatcher
{
public Dictionary<(string, string, string), ExecutionContext> Executors { get; } =
new Dictionary<(string, string, string), ExecutionContext>();
@ -28,4 +27,4 @@ namespace SignalRServiceExtension.Tests.Utils
throw new NotImplementedException();
}
}
}
}