Skip to content

Commit

Permalink
Akka.Serialization: INoSerializationVerificationNeeded does not han…
Browse files Browse the repository at this point in the history
…dle `IWrappedMessage` correctly (#7010)

* Added reproduction

`INoSerializationVerificationNeeded` does not handle `IWrappedMessage` correctly

* fixed issue

* added correct unwrapping method

* Fix dead letter behaviour change

* Fix implementation

---------

Co-authored-by: Gregorius Soedharmo <arkatufus@yahoo.com>
  • Loading branch information
Aaronontheweb and Arkatufus authored Dec 7, 2023
1 parent 93af6e4 commit 04630da
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 9 deletions.
76 changes: 76 additions & 0 deletions src/core/Akka.Tests/Serialization/SerializeAllMessagesSpec.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//-----------------------------------------------------------------------
// <copyright file="SerializeAllMessagesSpec.cs" company="Akka.NET Project">
// Copyright (C) 2009-2023 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2023 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Threading.Tasks;
using Akka.Actor;
using Akka.Event;
using Akka.TestKit;
using Xunit;
using Xunit.Abstractions;

namespace Akka.Tests.Serialization;

public class SerializeAllMessagesSpec : AkkaSpec
{
public SerializeAllMessagesSpec(ITestOutputHelper output) : base("akka.actor.serialize-messages = on", output)
{
}

private class MyMessage : INoSerializationVerificationNeeded
{
public MyMessage(Action myAction)
{
MyAction = myAction;
}

// add an unserializable member, such as a delegate
public Action MyAction { get; }
}

private class MyWrappedMessage : IWrappedMessage
{
public MyWrappedMessage(object message)
{
Message = message;
}

public object Message { get; }
}

// create an actor that will process a MyWrappedMessage and invoke the delegate
private class MyActor : ReceiveActor
{
public MyActor()
{
Receive<MyWrappedMessage>(msg =>
{
var myMessage = (MyMessage) msg.Message;
myMessage.MyAction();
});
}
}

[Fact]
public async Task Should_not_serialize_WrappedMessage_with_INoSerializationVerificationNeeded()
{
// Arrange
var myProbe = CreateTestProbe();
var action = () => { myProbe.Tell("worked"); };
var message = new MyMessage(action);
var wrappedMessage = new MyWrappedMessage(message);

var myActor = Sys.ActorOf(Props.Create(() => new MyActor()), "wrapped-message-actor");

await EventFilter.Error().ExpectAsync(0, async () =>
{
// Act
myActor.Tell(wrappedMessage);
await myProbe.ExpectMsgAsync("worked");
});
}
}
17 changes: 10 additions & 7 deletions src/core/Akka/Actor/ActorCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -516,11 +516,13 @@ public static IActorRef GetCurrentSenderOrNoSender()
return current != null ? current.Sender : ActorRefs.NoSender;
}

#nullable enable
private Envelope SerializeAndDeserialize(Envelope envelope)
{
DeadLetter deadLetter;
var unwrapped = (deadLetter = envelope.Message as DeadLetter) != null ? deadLetter.Message : envelope.Message;

// recursively unwraps message, no need to check for DeadLetter because DeadLetter inherits IWrappedMessage
var unwrapped = WrappedMessage.Unwrap(envelope.Message);

// don't do serialization verification if the underlying type doesn't require it
if (unwrapped is INoSerializationVerificationNeeded)
return envelope;

Expand All @@ -534,11 +536,12 @@ private Envelope SerializeAndDeserialize(Envelope envelope)
throw new SerializationException($"Failed to serialize and deserialize payload object [{unwrapped.GetType()}]. Envelope: [{envelope}], Actor type: [{Actor.GetType()}]", e);
}

if (deadLetter != null)
return new Envelope(new DeadLetter(deserializedMsg, deadLetter.Sender, deadLetter.Recipient), envelope.Sender);
return new Envelope(deserializedMsg, envelope.Sender);

// special case handling for DeadLetters
return envelope.Message is DeadLetter deadLetter
? new Envelope(new DeadLetter(deserializedMsg, deadLetter.Sender, deadLetter.Recipient), envelope.Sender)
: new Envelope(deserializedMsg, envelope.Sender);
}
#nullable restore

private object SerializeAndDeserializePayload(object obj)
{
Expand Down
6 changes: 4 additions & 2 deletions src/core/Akka/Actor/BuiltInActors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,10 @@ public static class WrappedMessage
{
public static object Unwrap(object message)
{
if (message is IWrappedMessage wm)
return Unwrap(wm.Message);
while (message is IWrappedMessage wm)
{
message = wm.Message;
}
return message;
}
}
Expand Down

0 comments on commit 04630da

Please sign in to comment.