From 44886cca964ffe8f2c9661b12a800cc35c3a5ad2 Mon Sep 17 00:00:00 2001 From: Artem Derevnjuk Date: Wed, 4 Oct 2023 16:21:29 +0400 Subject: [PATCH] fix(repeater): msgpack serialization fixes #165 --- .../Bus/DefaultRepeaterBusFactory.cs | 2 +- ...cketIoClient.cs => ISocketIoConnection.cs} | 4 +- ...ocketIoResponse.cs => ISocketIoMessage.cs} | 4 +- src/SecTester.Repeater/Bus/IncomingRequest.cs | 13 ++-- .../Bus/OutgoingResponse.cs | 18 +++--- src/SecTester.Repeater/Bus/RepeaterError.cs | 9 +++ src/SecTester.Repeater/Bus/RepeaterInfo.cs | 9 +++ src/SecTester.Repeater/Bus/RepeaterVersion.cs | 9 +++ ...ClientWrapper.cs => SocketIoConnection.cs} | 6 +- src/SecTester.Repeater/Bus/SocketIoMessage.cs | 22 +++++++ .../Bus/SocketIoRepeaterBus.cs | 34 +++++------ src/SecTester.Repeater/Runners/IRequest.cs | 10 ++-- src/SecTester.Repeater/Runners/IResponse.cs | 12 ++-- src/SecTester.Scan/Target.cs | 6 +- .../Bus/SocketIoRepeaterBusTests.cs | 60 +++++++++---------- 15 files changed, 131 insertions(+), 87 deletions(-) rename src/SecTester.Repeater/Bus/{ISocketIoClient.cs => ISocketIoConnection.cs} (68%) rename src/SecTester.Repeater/Bus/{ISocketIoResponse.cs => ISocketIoMessage.cs} (75%) create mode 100644 src/SecTester.Repeater/Bus/RepeaterError.cs create mode 100644 src/SecTester.Repeater/Bus/RepeaterInfo.cs create mode 100644 src/SecTester.Repeater/Bus/RepeaterVersion.cs rename src/SecTester.Repeater/Bus/{SocketIoClientWrapper.cs => SocketIoConnection.cs} (62%) create mode 100644 src/SecTester.Repeater/Bus/SocketIoMessage.cs diff --git a/src/SecTester.Repeater/Bus/DefaultRepeaterBusFactory.cs b/src/SecTester.Repeater/Bus/DefaultRepeaterBusFactory.cs index fc8844d..45f6a56 100644 --- a/src/SecTester.Repeater/Bus/DefaultRepeaterBusFactory.cs +++ b/src/SecTester.Repeater/Bus/DefaultRepeaterBusFactory.cs @@ -43,7 +43,7 @@ public IRepeaterBus Create(string repeaterId) { Serializer = new SocketIOMessagePackSerializer() }; - var wrapper = new SocketIoClientWrapper(client); + var wrapper = new SocketIoConnection(client); var scope = _scopeFactory.CreateAsyncScope(); var timerProvider = scope.ServiceProvider.GetRequiredService(); diff --git a/src/SecTester.Repeater/Bus/ISocketIoClient.cs b/src/SecTester.Repeater/Bus/ISocketIoConnection.cs similarity index 68% rename from src/SecTester.Repeater/Bus/ISocketIoClient.cs rename to src/SecTester.Repeater/Bus/ISocketIoConnection.cs index e8e6005..47b8c32 100644 --- a/src/SecTester.Repeater/Bus/ISocketIoClient.cs +++ b/src/SecTester.Repeater/Bus/ISocketIoConnection.cs @@ -3,12 +3,12 @@ namespace SecTester.Repeater.Bus; -internal interface ISocketIoClient : IDisposable +internal interface ISocketIoConnection : IDisposable { public bool Connected { get; } public Task Connect(); public Task Disconnect(); - public void On(string eventName, Action callback); + public void On(string eventName, Action callback); public void Off(string eventName); public Task EmitAsync(string eventName, params object[] data); } diff --git a/src/SecTester.Repeater/Bus/ISocketIoResponse.cs b/src/SecTester.Repeater/Bus/ISocketIoMessage.cs similarity index 75% rename from src/SecTester.Repeater/Bus/ISocketIoResponse.cs rename to src/SecTester.Repeater/Bus/ISocketIoMessage.cs index 03bdf61..a459aac 100644 --- a/src/SecTester.Repeater/Bus/ISocketIoResponse.cs +++ b/src/SecTester.Repeater/Bus/ISocketIoMessage.cs @@ -3,9 +3,9 @@ namespace SecTester.Repeater.Bus; -internal interface ISocketIoResponse +internal interface ISocketIoMessage { - public T GetValue(int i = 0); + public T GetValue(int index = 0); public Task CallbackAsync(params object[] data); public Task CallbackAsync(CancellationToken cancellationToken, params object[] data); } diff --git a/src/SecTester.Repeater/Bus/IncomingRequest.cs b/src/SecTester.Repeater/Bus/IncomingRequest.cs index 4a6dd2f..da65e3b 100644 --- a/src/SecTester.Repeater/Bus/IncomingRequest.cs +++ b/src/SecTester.Repeater/Bus/IncomingRequest.cs @@ -1,18 +1,19 @@ using System; using System.Collections.Generic; using System.Net.Http; +using MessagePack; using SecTester.Core.Bus; using SecTester.Repeater.Runners; namespace SecTester.Repeater.Bus; +[MessagePackObject(true)] public record IncomingRequest(Uri Url) : Event, IRequest { - public string? Body { get; init; } - public HttpMethod Method { get; init; } = HttpMethod.Get; - public Protocol Protocol { get; init; } = Protocol.Http; - public Uri Url { get; init; } = Url ?? throw new ArgumentNullException(nameof(Url)); - - public IEnumerable>> Headers { get; init; } = + public string? Body { get; set; } + public HttpMethod Method { get; set; } = HttpMethod.Get; + public Protocol Protocol { get; set; } = Protocol.Http; + public Uri Url { get; set; } = Url ?? throw new ArgumentNullException(nameof(Url)); + public IEnumerable>> Headers { get; set; } = new List>>(); } diff --git a/src/SecTester.Repeater/Bus/OutgoingResponse.cs b/src/SecTester.Repeater/Bus/OutgoingResponse.cs index 36a01d1..6a6dd3e 100644 --- a/src/SecTester.Repeater/Bus/OutgoingResponse.cs +++ b/src/SecTester.Repeater/Bus/OutgoingResponse.cs @@ -1,19 +1,17 @@ using System.Collections.Generic; +using MessagePack; using SecTester.Repeater.Runners; namespace SecTester.Repeater.Bus; +[MessagePackObject(true)] public record OutgoingResponse : IResponse { - public int? StatusCode { get; init; } - - public string? Body { get; init; } - public string? Message { get; init; } - - public string? ErrorCode { get; init; } - - public Protocol Protocol { get; init; } = Protocol.Http; - - public IEnumerable>> Headers { get; init; } = + public int? StatusCode { get; set; } + public string? Body { get; set; } + public string? Message { get; set; } + public string? ErrorCode { get; set; } + public Protocol Protocol { get; set; } = Protocol.Http; + public IEnumerable>> Headers { get; set; } = new List>>(); } diff --git a/src/SecTester.Repeater/Bus/RepeaterError.cs b/src/SecTester.Repeater/Bus/RepeaterError.cs new file mode 100644 index 0000000..eabc9e6 --- /dev/null +++ b/src/SecTester.Repeater/Bus/RepeaterError.cs @@ -0,0 +1,9 @@ +using MessagePack; + +namespace SecTester.Repeater.Bus; + +[MessagePackObject(true)] +public sealed record RepeaterError +{ + public string Message { get; set; } = null!; +} diff --git a/src/SecTester.Repeater/Bus/RepeaterInfo.cs b/src/SecTester.Repeater/Bus/RepeaterInfo.cs new file mode 100644 index 0000000..b76257b --- /dev/null +++ b/src/SecTester.Repeater/Bus/RepeaterInfo.cs @@ -0,0 +1,9 @@ +using MessagePack; + +namespace SecTester.Repeater.Bus; + +[MessagePackObject(true)] +public sealed record RepeaterInfo +{ + public string RepeaterId { get; set; } = null!; +} diff --git a/src/SecTester.Repeater/Bus/RepeaterVersion.cs b/src/SecTester.Repeater/Bus/RepeaterVersion.cs new file mode 100644 index 0000000..915aa21 --- /dev/null +++ b/src/SecTester.Repeater/Bus/RepeaterVersion.cs @@ -0,0 +1,9 @@ +using MessagePack; + +namespace SecTester.Repeater.Bus; + +[MessagePackObject(true)] +public sealed record RepeaterVersion +{ + public string Version { get; set; } = null!; +} diff --git a/src/SecTester.Repeater/Bus/SocketIoClientWrapper.cs b/src/SecTester.Repeater/Bus/SocketIoConnection.cs similarity index 62% rename from src/SecTester.Repeater/Bus/SocketIoClientWrapper.cs rename to src/SecTester.Repeater/Bus/SocketIoConnection.cs index 9e10b81..386ea50 100644 --- a/src/SecTester.Repeater/Bus/SocketIoClientWrapper.cs +++ b/src/SecTester.Repeater/Bus/SocketIoConnection.cs @@ -3,11 +3,11 @@ namespace SecTester.Repeater.Bus; -internal sealed class SocketIoClientWrapper : ISocketIoClient +internal sealed class SocketIoConnection : ISocketIoConnection { private readonly SocketIOClient.SocketIO _socketIo; - public SocketIoClientWrapper(SocketIOClient.SocketIO socketIo) => _socketIo = socketIo ?? throw new ArgumentNullException(nameof(socketIo)); + public SocketIoConnection(SocketIOClient.SocketIO socketIo) => _socketIo = socketIo ?? throw new ArgumentNullException(nameof(socketIo)); public void Dispose() { @@ -21,7 +21,7 @@ public void Dispose() public Task Disconnect() => _socketIo.DisconnectAsync(); - public void On(string eventName, Action callback) => _socketIo.On(eventName, x => callback(x as ISocketIoResponse)); + public void On(string eventName, Action callback) => _socketIo.On(eventName, x => callback(new SocketIoMessage(x))); public void Off(string eventName) => _socketIo.Off(eventName); diff --git a/src/SecTester.Repeater/Bus/SocketIoMessage.cs b/src/SecTester.Repeater/Bus/SocketIoMessage.cs new file mode 100644 index 0000000..d336db1 --- /dev/null +++ b/src/SecTester.Repeater/Bus/SocketIoMessage.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using SocketIOClient; + +namespace SecTester.Repeater.Bus; + +internal class SocketIoMessage : ISocketIoMessage +{ + private readonly SocketIOResponse _response; + + public SocketIoMessage(SocketIOResponse response) + { + _response = response ?? throw new ArgumentNullException(nameof(response)); + } + + public virtual T GetValue(int index = 0) => _response.GetValue(index); + + public virtual Task CallbackAsync(params object[] data) => _response.CallbackAsync(data); + + public virtual Task CallbackAsync(CancellationToken cancellationToken, params object[] data) => _response.CallbackAsync(cancellationToken, data); +} diff --git a/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs b/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs index c41714c..fa3fd44 100644 --- a/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs +++ b/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs @@ -12,33 +12,29 @@ internal sealed class SocketIoRepeaterBus : IRepeaterBus private static readonly TimeSpan PingInterval = TimeSpan.FromSeconds(10); private readonly ITimerProvider _heartbeat; - private readonly ISocketIoClient _client; + private readonly ISocketIoConnection _connection; private readonly ILogger _logger; private readonly SocketIoRepeaterBusOptions _options; - internal SocketIoRepeaterBus(SocketIoRepeaterBusOptions options, ISocketIoClient client, ITimerProvider heartbeat, ILogger logger) + internal SocketIoRepeaterBus(SocketIoRepeaterBusOptions options, ISocketIoConnection connection, ITimerProvider heartbeat, ILogger logger) { _options = options ?? throw new ArgumentNullException(nameof(options)); - _client = client ?? throw new ArgumentNullException(nameof(client)); + _connection = connection ?? throw new ArgumentNullException(nameof(connection)); _heartbeat = heartbeat ?? throw new ArgumentNullException(nameof(heartbeat)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } - internal sealed record RepeaterVersion(string Version); - internal sealed record RepeaterError(string Message); - internal sealed record RepeaterInfo(string RepeaterId); - public event Func>? RequestReceived; public event Action? ErrorOccurred; public event Action? UpgradeAvailable; public async Task Connect() { - if (_client is not { Connected: true }) + if (_connection is not { Connected: true }) { DelegateEvents(); - await _client.Connect().ConfigureAwait(false); + await _connection.Connect().ConfigureAwait(false); await SchedulePing().ConfigureAwait(false); @@ -48,19 +44,19 @@ public async Task Connect() private void DelegateEvents() { - _client.On("error", response => + _connection.On("error", response => { var err = response.GetValue(); ErrorOccurred?.Invoke(new(err.Message)); }); - _client.On("update-available", response => + _connection.On("update-available", response => { var version = response.GetValue(); UpgradeAvailable?.Invoke(new(version.Version)); }); - _client.On("request", async response => + _connection.On("request", async response => { if (RequestReceived == null) { @@ -76,15 +72,15 @@ private void DelegateEvents() public async ValueTask DisposeAsync() { - if (_client is { Connected: true }) + if (_connection is { Connected: true }) { _heartbeat.Elapsed -= Ping; _heartbeat.Stop(); - await _client.Disconnect().ConfigureAwait(false); + await _connection.Disconnect().ConfigureAwait(false); _logger.LogDebug("Repeater disconnected from {BaseUrl}", _options.BaseUrl); } - _client.Dispose(); + _connection.Dispose(); RequestReceived = null; ErrorOccurred = null; @@ -99,9 +95,9 @@ public async Task Deploy(string repeaterId, CancellationToken? cancellationToken { var tcs = new TaskCompletionSource(); - _client.On("deployed", response => tcs.TrySetResult(response.GetValue())); + _connection.On("deployed", response => tcs.TrySetResult(response.GetValue())); - await _client.EmitAsync("deploy", new RepeaterInfo(repeaterId)).ConfigureAwait(false); + await _connection.EmitAsync("deploy", new RepeaterInfo { RepeaterId = repeaterId }).ConfigureAwait(false); using var _ = cancellationToken?.Register(() => tcs.TrySetCanceled()); @@ -111,7 +107,7 @@ public async Task Deploy(string repeaterId, CancellationToken? cancellationToken } finally { - _client.Off("deployed"); + _connection.Off("deployed"); } } @@ -130,6 +126,6 @@ private async void Ping(object sender, ElapsedEventArgs args) private async Task Ping() { - await _client.EmitAsync("ping").ConfigureAwait(false); + await _connection.EmitAsync("ping").ConfigureAwait(false); } } diff --git a/src/SecTester.Repeater/Runners/IRequest.cs b/src/SecTester.Repeater/Runners/IRequest.cs index f77a5be..4e2d737 100644 --- a/src/SecTester.Repeater/Runners/IRequest.cs +++ b/src/SecTester.Repeater/Runners/IRequest.cs @@ -6,9 +6,9 @@ namespace SecTester.Repeater.Runners; public interface IRequest { - string? Body { get; init; } - HttpMethod Method { get; init; } - Protocol Protocol { get; init; } - Uri Url { get; init; } - IEnumerable>> Headers { get; init; } + string? Body { get; set; } + HttpMethod Method { get; set; } + Protocol Protocol { get; set; } + Uri Url { get; set; } + IEnumerable>> Headers { get; set; } } diff --git a/src/SecTester.Repeater/Runners/IResponse.cs b/src/SecTester.Repeater/Runners/IResponse.cs index 2d7c822..af9e56c 100644 --- a/src/SecTester.Repeater/Runners/IResponse.cs +++ b/src/SecTester.Repeater/Runners/IResponse.cs @@ -4,10 +4,10 @@ namespace SecTester.Repeater.Runners; public interface IResponse { - int? StatusCode { get; init; } - string? Body { get; init; } - string? Message { get; init; } - string? ErrorCode { get; init; } - Protocol Protocol { get; init; } - IEnumerable>> Headers { get; init; } + int? StatusCode { get; set; } + string? Body { get; set; } + string? Message { get; set; } + string? ErrorCode { get; set; } + Protocol Protocol { get; set; } + IEnumerable>> Headers { get; set; } } diff --git a/src/SecTester.Scan/Target.cs b/src/SecTester.Scan/Target.cs index 4a612b0..db9047d 100644 --- a/src/SecTester.Scan/Target.cs +++ b/src/SecTester.Scan/Target.cs @@ -119,7 +119,7 @@ public Target WithBody(FormUrlEncodedContent value) { return WithBody(value, async () => { - var text = await value.ReadAsStringAsync().ConfigureAwait(true); + var text = await value.ReadAsStringAsync().ConfigureAwait(false); var query = UrlUtils.ParseQuery(text); var parameters = query.Select(k => new PostDataParameter(k.Key, k.Value)).ToList(); @@ -140,7 +140,7 @@ public Target WithBody(MultipartContent value) { return WithBody(value, async () => { - var text = await value.ReadAsStringAsync().ConfigureAwait(true); + var text = await value.ReadAsStringAsync().ConfigureAwait(false); var tasks = value.Select(async k => { var contentDisposition = k.Headers.ContentDisposition; @@ -172,7 +172,7 @@ public Target WithBody(HttpContent value) { return WithBody(value, async () => { - var text = await value.ReadAsStringAsync().ConfigureAwait(true); + var text = await value.ReadAsStringAsync().ConfigureAwait(false); return new PostData { diff --git a/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs b/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs index ff80976..1dc7206 100644 --- a/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs +++ b/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs @@ -6,21 +6,21 @@ public sealed class SocketIoRepeaterBusTests : IDisposable private static readonly Uri Url = new("http://example.com"); private static readonly SocketIoRepeaterBusOptions Options = new(Url); - private readonly ISocketIoClient _client = Substitute.For(); + private readonly ISocketIoConnection _connection = Substitute.For(); private readonly ITimerProvider _heartbeat = Substitute.For(); private readonly ILogger _logger = Substitute.For>(); - private readonly ISocketIoResponse _socketIoResponse = Substitute.For(); + private readonly ISocketIoMessage _socketIoMessage = Substitute.For(); private readonly SocketIoRepeaterBus _sut; public SocketIoRepeaterBusTests() { - _sut = new SocketIoRepeaterBus(Options, _client, _heartbeat, _logger); + _sut = new SocketIoRepeaterBus(Options, _connection, _heartbeat, _logger); } public void Dispose() { - _socketIoResponse.ClearSubstitute(); - _client.ClearSubstitute(); + _socketIoMessage.ClearSubstitute(); + _connection.ClearSubstitute(); _heartbeat.ClearSubstitute(); _logger.ClearSubstitute(); @@ -35,16 +35,16 @@ public async Task RequestReceived_ExecutesHandler() { StatusCode = 204 }; - _client.Connect().Returns(Task.CompletedTask); - _socketIoResponse.GetValue().Returns(new IncomingRequest(Url)); - _client.On("request", Arg.Invoke(_socketIoResponse)); + _connection.Connect().Returns(Task.CompletedTask); + _socketIoMessage.GetValue().Returns(new IncomingRequest(Url)); + _connection.On("request", Arg.Invoke(_socketIoMessage)); _sut.RequestReceived += _ => Task.FromResult(result); // act await _sut.Connect(); // assert - await _socketIoResponse.Received().CallbackAsync(Arg.Any(), result); + await _socketIoMessage.Received().CallbackAsync(Arg.Any(), result); } [Fact] @@ -53,9 +53,9 @@ public async Task ErrorOccurred_ExecutesHandler() // arrange const string msg = "Something went wrong"; Exception? result = null; - _client.Connect().Returns(Task.CompletedTask); - _socketIoResponse.GetValue().Returns(new SocketIoRepeaterBus.RepeaterError(msg)); - _client.On("error", Arg.Invoke(_socketIoResponse)); + _connection.Connect().Returns(Task.CompletedTask); + _socketIoMessage.GetValue().Returns(new RepeaterError { Message = msg }); + _connection.On("error", Arg.Invoke(_socketIoMessage)); _sut.ErrorOccurred += err => { result = err; @@ -73,9 +73,9 @@ public async Task UpgradeAvailable_ExecutesHandler() { // arrange Version? result = null; - _client.Connect().Returns(Task.CompletedTask); - _socketIoResponse.GetValue().Returns(new SocketIoRepeaterBus.RepeaterVersion("1.1.1")); - _client.On("update-available", Arg.Invoke(_socketIoResponse)); + _connection.Connect().Returns(Task.CompletedTask); + _socketIoMessage.GetValue().Returns(new RepeaterVersion { Version = "1.1.1" }); + _connection.On("update-available", Arg.Invoke(_socketIoMessage)); _sut.UpgradeAvailable += version => { result = version; @@ -92,33 +92,33 @@ public async Task UpgradeAvailable_ExecutesHandler() public async Task Connect_Success() { // arrange - _client.Connect().Returns(Task.CompletedTask); + _connection.Connect().Returns(Task.CompletedTask); // act await _sut.Connect(); // assert - await _client.Received().Connect(); + await _connection.Received().Connect(); } [Fact] public async Task Connect_AlreadyConnected_DoNothing() { // arrange - _client.Connected.Returns(true); + _connection.Connected.Returns(true); // act await _sut.Connect(); // assert - await _client.DidNotReceive().Connect(); + await _connection.DidNotReceive().Connect(); } [Fact] public async Task Connect_SchedulesPing() { // arrange - _client.Connect().Returns(Task.CompletedTask); + _connection.Connect().Returns(Task.CompletedTask); // act await _sut.Connect(); @@ -133,7 +133,7 @@ public async Task Connect_ShouldSendPingMessage() { // arrange var elapsedEventArgs = EventArgs.Empty as ElapsedEventArgs; - _client.Connect().Returns(Task.CompletedTask); + _connection.Connect().Returns(Task.CompletedTask); await _sut.Connect(); // act @@ -141,20 +141,20 @@ public async Task Connect_ShouldSendPingMessage() // assert _heartbeat.Interval.Should().BeGreaterOrEqualTo(10_000); - await _client.Received(2).EmitAsync("ping"); + await _connection.Received(2).EmitAsync("ping"); } [Fact] public async Task Deploy_Success() { // arrange - _client.On("deployed", Arg.Invoke(_socketIoResponse)); + _connection.On("deployed", Arg.Invoke(_socketIoMessage)); // act await _sut.Deploy(RepeaterId); // assert - await _client.Received().EmitAsync("deploy", Arg.Is(x => x.RepeaterId.Equals(RepeaterId, StringComparison.OrdinalIgnoreCase))); + await _connection.Received().EmitAsync("deploy", Arg.Is(x => x.RepeaterId.Equals(RepeaterId, StringComparison.OrdinalIgnoreCase))); } [Fact] @@ -175,14 +175,14 @@ public async Task Deploy_GivenCancellationToken_ThrowsError() public async Task DisposeAsync_Success() { // arrange - _client.Connected.Returns(true); + _connection.Connected.Returns(true); // act await _sut.DisposeAsync(); // assert - await _client.Received().Disconnect(); - _client.Received().Dispose(); + await _connection.Received().Disconnect(); + _connection.Received().Dispose(); } [Fact] @@ -192,15 +192,15 @@ public async Task DisposeAsync_NotConnectedYet_Success() await _sut.DisposeAsync(); // assert - await _client.DidNotReceive().Disconnect(); - _client.Received().Dispose(); + await _connection.DidNotReceive().Disconnect(); + _connection.Received().Dispose(); } [Fact] public async Task DisposeAsync_StopsPingMessages() { // arrange - _client.Connected.Returns(true); + _connection.Connected.Returns(true); // act await _sut.DisposeAsync();