Skip to content

Commit

Permalink
Merge pull request #798 from Cysharp/feature/ExposeHubMethodAttribute…
Browse files Browse the repository at this point in the history
…Lookup

Expose attributes lookup of Hub methods
  • Loading branch information
mayuki authored Jul 1, 2024
2 parents 133d110 + 5f9614a commit 0c70646
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 25 deletions.
5 changes: 2 additions & 3 deletions src/MagicOnion.Server/Hubs/StreamingHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,10 @@ async ValueTask ProcessRequestAsync(UniqueHashDictionary<StreamingHubHandler> ha
// Create a context for each call to the hub method.
var context = StreamingHubContextPool.Shared.Get();
context.Initialize(
serviceContext: (IStreamingServiceContext<StreamingHubPayload, StreamingHubPayload>)Context,
handler: handler,
streamingServiceContext: (IStreamingServiceContext<StreamingHubPayload, StreamingHubPayload>)Context,
hubInstance: this,
request: body,
path: handler.ToString(),
methodId: methodId,
messageId: messageId,
timestamp: DateTime.UtcNow
);
Expand Down
17 changes: 9 additions & 8 deletions src/MagicOnion.Server/Hubs/StreamingHubContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class StreamingHubContext
{
IStreamingServiceContext<StreamingHubPayload, StreamingHubPayload> streamingServiceContext = default!;
ConcurrentDictionary<string, object>? items;
StreamingHubHandler handler = default!;

/// <summary>Object storage per invoke.</summary>
public ConcurrentDictionary<string, object> Items
Expand All @@ -49,42 +50,42 @@ public ConcurrentDictionary<string, object> Items
}
}

public string Path => handler.ToString();
public ILookup<Type, Attribute> AttributeLookup => handler.AttributeLookup;

public object HubInstance { get; private set; } = default!;

public ReadOnlyMemory<byte> Request { get; private set; }
public string Path { get; private set; } = default!;
public DateTime Timestamp { get; private set; }

public Guid ConnectionId => streamingServiceContext.ContextId;

public IServiceContext ServiceContext => streamingServiceContext;

internal int MessageId { get; private set; }
internal int MethodId { get; private set; }
internal int MethodId => handler.MethodId;

internal int ResponseSize { get; private set; } = -1;
internal Type? ResponseType { get; private set; }

internal void Initialize(IStreamingServiceContext<StreamingHubPayload, StreamingHubPayload> serviceContext, object hubInstance, ReadOnlyMemory<byte> request, string path, DateTime timestamp, int messageId, int methodId)
internal void Initialize(StreamingHubHandler handler, IStreamingServiceContext<StreamingHubPayload, StreamingHubPayload> streamingServiceContext, object hubInstance, ReadOnlyMemory<byte> request, DateTime timestamp, int messageId)
{
streamingServiceContext = serviceContext;
this.handler = handler;
this.streamingServiceContext = streamingServiceContext;
HubInstance = hubInstance;
Request = request;
Path = path;
Timestamp = timestamp;
MessageId = messageId;
MethodId = methodId;
}

internal void Uninitialize()
{
handler = default!;
streamingServiceContext = default!;
HubInstance = default!;
Request = default!;
Path = default!;
Timestamp = default!;
MessageId = default!;
MethodId = default!;
ResponseSize = -1;
items?.Clear();
}
Expand Down
57 changes: 43 additions & 14 deletions tests/MagicOnion.Server.Tests/StreamingHubHandlerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public async Task Parameterless_Returns_Task()
// Act
var handler = new StreamingHubHandler(hubType, hubMethod, new StreamingHubHandlerOptions(new MagicOnionOptions()), serviceProvider);
var ctx = new StreamingHubContext();
ctx.Initialize(fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(Nil.Default), string.Empty, DateTime.Now, 0, 0);
ctx.Initialize(handler, fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(Nil.Default), DateTime.Now, 0);
await handler.MethodBody.Invoke(ctx);

// Assert
Expand Down Expand Up @@ -61,7 +61,7 @@ public async Task Parameterless_Returns_TaskOfInt32()
// Act
var handler = new StreamingHubHandler(hubType, hubMethod, new StreamingHubHandlerOptions(new MagicOnionOptions()), serviceProvider);
var ctx = new StreamingHubContext();
ctx.Initialize(fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(Nil.Default), string.Empty, DateTime.Now, 0, 0);
ctx.Initialize(handler, fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(Nil.Default), DateTime.Now, 0);
await handler.MethodBody.Invoke(ctx);

// Assert
Expand Down Expand Up @@ -95,7 +95,7 @@ public async Task Parameterless_Returns_ValueTask()
// Act
var handler = new StreamingHubHandler(hubType, hubMethod, new StreamingHubHandlerOptions(new MagicOnionOptions()), serviceProvider);
var ctx = new StreamingHubContext();
ctx.Initialize(fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(Nil.Default), string.Empty, DateTime.Now, 0, 0);
ctx.Initialize(handler, fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(Nil.Default), DateTime.Now, 0);
await handler.MethodBody.Invoke(ctx);

// Assert
Expand Down Expand Up @@ -130,7 +130,7 @@ public async Task Parameterless_Returns_ValueTaskOfInt32()
// Act
var handler = new StreamingHubHandler(hubType, hubMethod, new StreamingHubHandlerOptions(new MagicOnionOptions()), serviceProvider);
var ctx = new StreamingHubContext();
ctx.Initialize(fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(Nil.Default), string.Empty, DateTime.Now, 0, 0);
ctx.Initialize(handler, fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(Nil.Default), DateTime.Now, 0);
await handler.MethodBody.Invoke(ctx);

// Assert
Expand Down Expand Up @@ -165,7 +165,7 @@ public async Task Parameter_Single_Returns_Task()
// Act
var handler = new StreamingHubHandler(hubType, hubMethod, new StreamingHubHandlerOptions(new MagicOnionOptions()), serviceProvider);
var ctx = new StreamingHubContext();
ctx.Initialize(fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(12345), string.Empty, DateTime.Now, 0, 0);
ctx.Initialize(handler, fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(12345), DateTime.Now, 0);
await handler.MethodBody.Invoke(ctx);

// Assert
Expand Down Expand Up @@ -200,7 +200,7 @@ public async Task Parameter_Multiple_Returns_Task()
// Act
var handler = new StreamingHubHandler(hubType, hubMethod, new StreamingHubHandlerOptions(new MagicOnionOptions()), serviceProvider);
var ctx = new StreamingHubContext();
ctx.Initialize(fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(new DynamicArgumentTuple<int, string, bool>(12345, "テスト", true)), string.Empty, DateTime.Now, 0, 0);
ctx.Initialize(handler, fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(new DynamicArgumentTuple<int, string, bool>(12345, "テスト", true)), DateTime.Now, 0);
await handler.MethodBody.Invoke(ctx);

// Assert
Expand Down Expand Up @@ -234,7 +234,7 @@ public async Task Parameter_Multiple_Returns_TaskOfInt32()
// Act
var handler = new StreamingHubHandler(hubType, hubMethod, new StreamingHubHandlerOptions(new MagicOnionOptions()), serviceProvider);
var ctx = new StreamingHubContext();
ctx.Initialize(fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(new DynamicArgumentTuple<int, string, bool>(12345, "テスト", true)), string.Empty, DateTime.Now, 0, 0);
ctx.Initialize(handler, fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(new DynamicArgumentTuple<int, string, bool>(12345, "テスト", true)), DateTime.Now, 0);
await handler.MethodBody.Invoke(ctx);

// Assert
Expand Down Expand Up @@ -270,7 +270,7 @@ public async Task CallRepeated_Parameter_Multiple_Returns_TaskOfInt32()
for (var i = 0; i < 3; i++)
{
var ctx = new StreamingHubContext();
ctx.Initialize(fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(new DynamicArgumentTuple<int, string, bool>(i, $"テスト{i}", i % 2 == 0)), string.Empty, DateTime.Now, i * 1000, 0);
ctx.Initialize(handler, fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(new DynamicArgumentTuple<int, string, bool>(i, $"テスト{i}", i % 2 == 0)), DateTime.Now, i * 1000);
await handler.MethodBody.Invoke(ctx);
}

Expand All @@ -286,7 +286,7 @@ byte[] BuildMessage(int messageId, int retVal)
var writer = new MessagePackWriter(buffer);
writer.WriteArrayHeader(3);
writer.Write(messageId);
writer.Write(0 /* MethodId - Fixed */);
writer.Write(FNV1A32.GetHashCode(nameof(StreamingHubHandlerTestHub.Method_Parameter_Multiple_Returns_TaskOfInt32)) /* MethodId */);
MessagePackSerializer.Serialize(ref writer, retVal);
writer.Flush();
return buffer.WrittenMemory.ToArray();
Expand All @@ -310,7 +310,7 @@ public async Task Parameterless_Void()
// Act
var handler = new StreamingHubHandler(hubType, hubMethod, new StreamingHubHandlerOptions(new MagicOnionOptions()), serviceProvider);
var ctx = new StreamingHubContext();
ctx.Initialize(fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(Nil.Default), string.Empty, DateTime.Now, 0, 0);
ctx.Initialize(handler, fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(Nil.Default), DateTime.Now, 0);
await handler.MethodBody.Invoke(ctx);

// Assert
Expand Down Expand Up @@ -345,7 +345,7 @@ public async Task Parameter_Single_Void()
// Act
var handler = new StreamingHubHandler(hubType, hubMethod, new StreamingHubHandlerOptions(new MagicOnionOptions()), serviceProvider);
var ctx = new StreamingHubContext();
ctx.Initialize(fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(12345), string.Empty, DateTime.Now, 0, 0);
ctx.Initialize(handler, fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(12345), DateTime.Now, 0);
await handler.MethodBody.Invoke(ctx);

// Assert
Expand Down Expand Up @@ -380,7 +380,7 @@ public async Task Parameter_Multiple_Void()
// Act
var handler = new StreamingHubHandler(hubType, hubMethod, new StreamingHubHandlerOptions(new MagicOnionOptions()), serviceProvider);
var ctx = new StreamingHubContext();
ctx.Initialize(fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(new DynamicArgumentTuple<int, string, bool>(12345, "テスト", true)), string.Empty, DateTime.Now, 0, 0);
ctx.Initialize(handler, fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(new DynamicArgumentTuple<int, string, bool>(12345, "テスト", true)), DateTime.Now, 0);
await handler.MethodBody.Invoke(ctx);

// Assert
Expand Down Expand Up @@ -415,7 +415,7 @@ public async Task Parameter_Multiple_Void_Without_MessageId()
// Act
var handler = new StreamingHubHandler(hubType, hubMethod, new StreamingHubHandlerOptions(new MagicOnionOptions()), serviceProvider);
var ctx = new StreamingHubContext();
ctx.Initialize(fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(new DynamicArgumentTuple<int, string, bool>(12345, "テスト", true)), string.Empty, DateTime.Now, -1 /* The client requires no response */, 0);
ctx.Initialize(handler, fakeStreamingHubContext, hubInstance, MessagePackSerializer.Serialize(new DynamicArgumentTuple<int, string, bool>(12345, "テスト", true)), DateTime.Now, -1 /* The client requires no response */);
await handler.MethodBody.Invoke(ctx);

// Assert
Expand Down Expand Up @@ -443,7 +443,7 @@ public async Task UseCustomMessageSerializer()
MessageSerializer = XorMessagePackMagicOnionSerializerProvider.Instance,
}), serviceProvider);
var ctx = new StreamingHubContext();
ctx.Initialize(fakeStreamingHubContext, hubInstance, bufferWriter.WrittenMemory.ToArray(), string.Empty, DateTime.Now, 0, 0);
ctx.Initialize(handler, fakeStreamingHubContext, hubInstance, bufferWriter.WrittenMemory.ToArray(), DateTime.Now, 0);
await handler.MethodBody.Invoke(ctx);

// Assert
Expand All @@ -463,6 +463,24 @@ byte[] BuildMessage()
fakeStreamingHubContext.Responses[0].Memory.ToArray().Should().Equal(BuildMessage());
}

[Fact]
public async Task MethodAttributeLookup()
{
// Arrange
var services = new ServiceCollection();
var serviceProvider = services.BuildServiceProvider();
var hubType = typeof(StreamingHubHandlerTestHub);
var hubMethod = hubType.GetMethod(nameof(StreamingHubHandlerTestHub.Method_Attribute))!;

// Act
var handler = new StreamingHubHandler(hubType, hubMethod, new StreamingHubHandlerOptions(new MagicOnionOptions()), serviceProvider);

// Assert
Assert.NotEmpty(handler.AttributeLookup);
Assert.NotEmpty(handler.AttributeLookup[typeof(CustomMethodAttribute)]);
Assert.NotEmpty(handler.AttributeLookup[typeof(CustomHubAttribute)]);
}

interface IStreamingHubHandlerTestHubReceiver
{
}
Expand All @@ -482,8 +500,16 @@ interface IStreamingHubHandlerTestHub : IStreamingHub<IStreamingHubHandlerTestHu
void Method_Parameterless_Void();
void Method_Parameter_Single_Void(int arg0);
void Method_Parameter_Multiple_Void(int arg0, string arg1, bool arg2);

Task Method_Attribute();
}

[AttributeUsage(AttributeTargets.Class)]
class CustomHubAttribute : Attribute;
[AttributeUsage(AttributeTargets.Method)]
class CustomMethodAttribute : Attribute;

[CustomHub]
class StreamingHubHandlerTestHub : IStreamingHubHandlerTestHub
{
public List<string> Results { get; } = new List<string>();
Expand Down Expand Up @@ -548,5 +574,8 @@ public void Method_Parameter_Multiple_Void(int arg0, string arg1, bool arg2)
{
Results.Add(nameof(Method_Parameter_Multiple_Void) + $"({arg0},{arg1},{arg2}) called.");
}

[CustomMethod]
public Task Method_Attribute() => Task.CompletedTask;
}
}

0 comments on commit 0c70646

Please sign in to comment.