Skip to content

Commit

Permalink
Merge pull request #37 from reown-com/chore/upgrade-ws-lib
Browse files Browse the repository at this point in the history
chore: upgrade websocket library
  • Loading branch information
skibitsky authored Dec 18, 2024
2 parents 554c61c + 9a3b0e5 commit dc95a15
Show file tree
Hide file tree
Showing 22 changed files with 1,138 additions and 973 deletions.
7 changes: 5 additions & 2 deletions .github/actions/test-dotnet/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,22 @@ inputs:
project-id:
description: 'Reown project id'
required: true
dotnet-version:
description: 'The .NET version to use for testing'
required: true

runs:
using: "composite"
steps:
- name: Run tests
if: inputs.type == 'unit-tests'
shell: bash
run: dotnet test Reown.NoUnity.slnf --verbosity minimal --filter Category=unit
run: dotnet test Reown.NoUnity.slnf --verbosity minimal --filter Category=unit --framework net$(echo ${{ inputs.dotnet-version }} | cut -d'.' -f1).0

- name: Run integration tests
if: inputs.type == 'integration-tests'
shell: bash
env:
RELAY_ENDPOINT: ${{ inputs.relay-endpoint }}
PROJECT_ID: ${{ inputs.project-id }}
run: dotnet test Reown.NoUnity.slnf --verbosity detailed --filter Category=integration
run: dotnet test Reown.NoUnity.slnf --verbosity normal --filter Category=integration --framework net$(echo ${{ inputs.dotnet-version }} | cut -d'.' -f1).0
5 changes: 3 additions & 2 deletions .github/workflows/dotnet-build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
dotnet-version: [7.0.x, 8.0.x]
dotnet-version: [8.0.x]
test-type: [unit-tests, integration-tests]
runs-on: ${{ matrix.os }}

Expand All @@ -27,4 +27,5 @@ jobs:
uses: ./.github/actions/test-dotnet
with:
type: ${{ matrix.test-type }}
project-id: ${{ secrets.PROJECT_ID }}
project-id: ${{ secrets.PROJECT_ID }}
dotnet-version: ${{ matrix.dotnet-version }}
5 changes: 3 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
<PackageVersion Include="Nethereum.Web3" Version="4.26.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Portable.BouncyCastle" Version="1.9.0" />
<PackageVersion Include="Websocket.Client" Version="4.7.0" />
<PackageVersion Include="Websocket.Client" Version="5.1.2" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageVersion Include="xunit.abstractions" Version="2.0.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
99 changes: 58 additions & 41 deletions src/Reown.Core.Network.WebSocket/WebsocketConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Threading.Tasks;
using Newtonsoft.Json;
using Reown.Core.Common;
using Reown.Core.Common.Logging;
using Reown.Core.Common.Utils;
using Reown.Core.Network.Models;
using Websocket.Client;
Expand All @@ -17,26 +18,26 @@ public class WebsocketConnection : IJsonRpcConnection, IModule
{
private const string AddressNotFoundError = "getaddrinfo ENOTFOUND";
private const string ConnectionRefusedError = "connect ECONNREFUSED";
private readonly Guid _context;
private readonly string _context;
private readonly ILogger _logger;
private WebsocketClient _socket;
protected bool Disposed;

/// <summary>
/// The Open timeout
/// </summary>
public TimeSpan OpenTimeout = TimeSpan.FromSeconds(60);
private IDisposable _messageSubscription;
private IDisposable _disconnectionSubscription;
private bool _disposed;

/// <summary>
/// Create a new websocket connection that will connect to the given URL
/// </summary>
/// <param name="url">The URL to connect to</param>
/// <param name="context">The context of the root module</param>
/// <exception cref="ArgumentException">If the given URL is invalid</exception>
public WebsocketConnection(string url)
public WebsocketConnection(string url, string context = null)
{
if (!Validation.IsWsUrl(url))
throw new ArgumentException("Provided URL is not compatible with WebSocket connection: " + url);

_context = Guid.NewGuid();
_context = context ?? Guid.NewGuid().ToString();
_logger = ReownLogger.WithContext(Context);
Url = url;
}

Expand All @@ -47,6 +48,14 @@ public WebsocketConnection(string url)

public bool IsPaused { get; internal set; }

/// <summary>
/// The Open timeout
/// </summary>
public TimeSpan OpenTimeout
{
get => TimeSpan.FromSeconds(60);
}

public event EventHandler<string> PayloadReceived;
public event EventHandler Closed;
public event EventHandler<Exception> ErrorReceived;
Expand Down Expand Up @@ -112,57 +121,58 @@ public async Task Close()
/// <typeparam name="T">The type of the Json RPC request parameter</typeparam>
public async Task SendRequest<T>(IJsonRpcRequest<T> requestPayload, object context)
{
if (_socket == null)
_socket = await Register(Url);
_socket ??= await Register(Url);

try
{
_logger.Log("Sending request over websocket");
_socket.Send(JsonConvert.SerializeObject(requestPayload));
}
catch (Exception e)
{
_logger.Log($"Error sending request: {e.Message}");
OnError<T>(requestPayload, e);
}
}

/// <summary>
/// Send a Json RPC response through this websocket connection, using the given context
/// </summary>
/// <param name="requestPayload">The response payload to encode and send</param>
/// <param name="responsePayload">The response payload to encode and send</param>
/// <param name="context">The context to use when sending</param>
/// <typeparam name="T">The type of the Json RPC response result</typeparam>
public async Task SendResult<T>(IJsonRpcResult<T> requestPayload, object context)
public async Task SendResult<T>(IJsonRpcResult<T> responsePayload, object context)
{
if (_socket == null)
_socket = await Register(Url);
_socket ??= await Register(Url);

try
{
_socket.Send(JsonConvert.SerializeObject(requestPayload));
_socket.Send(JsonConvert.SerializeObject(responsePayload));
}
catch (Exception e)
{
OnError<T>(requestPayload, e);
OnError<T>(responsePayload, e);
}
}

/// <summary>
/// Send a Json RPC error response through this websocket connection, using the given context
/// Send a JSON RPC error. This function does not return or wait for response. JSON RPC errors do not receive
/// any response and therefore do not trigger any events
/// </summary>
/// <param name="requestPayload">The error response payload to encode and send</param>
/// <param name="context">The context to use when sending</param>
public async Task SendError(IJsonRpcError requestPayload, object context)
/// <param name="errorPayload">The error to send</param>
/// <param name="context">The current context</param>
/// <returns>A task that is performing the send</returns>
public async Task SendError(IJsonRpcError errorPayload, object context)
{
if (_socket == null)
_socket = await Register(Url);
_socket ??= await Register(Url);

try
{
_socket.Send(JsonConvert.SerializeObject(requestPayload));
_socket.Send(JsonConvert.SerializeObject(errorPayload));
}
catch (Exception e)
{
OnError<object>(requestPayload, e);
OnError<object>(errorPayload, e);
}
}

Expand All @@ -177,15 +187,15 @@ public void Dispose()
/// </summary>
public string Name
{
get => "websocket-connection";
get => "ws-connection";
}

/// <summary>
/// The context string of this Websocket module
/// </summary>
public string Context
{
get => _context.ToString();
get => $"{_context}-{Name}";
}

private async Task<WebsocketClient> Register(string url)
Expand All @@ -195,14 +205,14 @@ private async Task<WebsocketClient> Register(string url)
throw new ArgumentException("Provided URL is not compatible with WebSocket connection: " + url);
}

_logger.Log($"Register new WS connection. Is already connecting: {Connecting}");

if (Connecting)
{
var registeringTask =
new TaskCompletionSource<WebsocketClient>(TaskCreationOptions.None);
var registeringTask = new TaskCompletionSource<WebsocketClient>(TaskCreationOptions.None);

RegisterErrored.ListenOnce((sender, args) => { registeringTask.SetException(args); });

Opened.ListenOnce((sender, args) => { registeringTask.SetResult((WebsocketClient)args); });
RegisterErrored.ListenOnce((sender, args) => registeringTask.SetException(args));
Opened.ListenOnce((sender, args) => registeringTask.SetResult((WebsocketClient)args));

await registeringTask.Task;

Expand Down Expand Up @@ -235,8 +245,8 @@ private void OnOpen(WebsocketClient socket)
if (socket == null)
return;

socket.MessageReceived.Subscribe(OnPayload);
socket.DisconnectionHappened.Subscribe(OnDisconnect);
_messageSubscription = socket.MessageReceived.Subscribe(OnPayload);
_disconnectionSubscription = socket.DisconnectionHappened.Subscribe(OnDisconnect);

_socket = socket;
Connecting = false;
Expand All @@ -256,7 +266,8 @@ private void OnClose(DisconnectionInfo obj)
if (_socket == null)
return;

//_socket.Dispose();
_logger.Log($"Connection closed. Close status: {obj.CloseStatus?.ToString() ?? "-- "}. Exception message: {obj.Exception?.Message ?? "--"}");

_socket = null;
Connecting = false;
Closed?.Invoke(this, EventArgs.Empty);
Expand All @@ -276,24 +287,30 @@ private void OnPayload(ResponseMessage obj)
return;
}

if (string.IsNullOrWhiteSpace(json)) return;

//Console.WriteLine($"[{Name}] Got payload {json}");
if (string.IsNullOrWhiteSpace(json))
return;

PayloadReceived?.Invoke(this, json);
}

protected virtual void Dispose(bool disposing)
{
if (Disposed)
if (_disposed)
return;

if (disposing)
{
_socket.Dispose();
_messageSubscription?.Dispose();
_disconnectionSubscription?.Dispose();

if (_socket != null)
{
_socket.Dispose();
_socket = null;
}
}

Disposed = true;
_disposed = true;
}

private void OnError<T>(IJsonRpcPayload ogPayload, Exception e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ namespace Reown.Core.Network.Websocket
{
public class WebsocketConnectionBuilder : IConnectionBuilder
{
public Task<IJsonRpcConnection> CreateConnection(string url)
public Task<IJsonRpcConnection> CreateConnection(string url, string context = null)
{
return Task.FromResult<IJsonRpcConnection>(new WebsocketConnection(url));
return Task.FromResult<IJsonRpcConnection>(new WebsocketConnection(url, context));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ namespace Reown.Core.Network.Interfaces
{
public interface IConnectionBuilder
{
Task<IJsonRpcConnection> CreateConnection(string url);
Task<IJsonRpcConnection> CreateConnection(string url, string context = null);
}
}
20 changes: 10 additions & 10 deletions src/Reown.Core.Network/Runtime/JsonRpcProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ namespace Reown.Core.Network
/// </summary>
public class JsonRpcProvider : IJsonRpcProvider
{
private readonly Guid _context;
private readonly string _context;
private readonly GenericEventHolder _jsonResponseEventHolder = new();
private readonly ILogger _logger;
private TaskCompletionSource<bool> _connecting = new();
private bool _connectingStarted;
private bool _hasRegisteredEventListeners;
Expand All @@ -26,9 +27,11 @@ public class JsonRpcProvider : IJsonRpcProvider
/// Create a new JsonRpcProvider with the given connection
/// </summary>
/// <param name="connection">The IJsonRpcConnection to use</param>
public JsonRpcProvider(IJsonRpcConnection connection)
public JsonRpcProvider(IJsonRpcConnection connection, string context = null)
{
_context = Guid.NewGuid();
_context = context ?? Guid.NewGuid().ToString();
_logger = ReownLogger.WithContext(Context);

Connection = connection;
if (Connection.Connected)
{
Expand Down Expand Up @@ -57,9 +60,7 @@ public string Name
/// </summary>
public string Context
{
get =>
//TODO Get context from logger
_context.ToString();
get => $"{_context}-{Name}";
}

/// <summary>
Expand Down Expand Up @@ -184,14 +185,14 @@ public async Task Disconnect()
/// <returns>A Task that will resolve when a response is received</returns>
public async Task<TR> Request<T, TR>(IRequestArguments<T> requestArgs, object context = null)
{
ReownLogger.Log("[JsonRpcProvider] Checking if connected");
_logger.Log("Checking if connected");
if (IsConnecting)
{
await _connecting.Task;
}
else if (!_connectingStarted && !Connection.Connected)
{
ReownLogger.Log("[JsonRpcProvider] Not connected, connecting now");
_logger.Log("Not connected, connecting now");
await Connect(Connection);
}

Expand Down Expand Up @@ -247,8 +248,7 @@ public async Task<TR> Request<T, TR>(IRequestArguments<T> requestArgs, object co

_lastId = request.Id;

ReownLogger.Log(
$"[JsonRpcProvider] Sending request {request.Method} with data {JsonConvert.SerializeObject(request)}");
_logger.Log($"Sending request {request.Method} with data {JsonConvert.SerializeObject(request)}");
await Connection.SendRequest(request, context);

await requestTask.Task;
Expand Down
Loading

0 comments on commit dc95a15

Please sign in to comment.