Skip to content

Commit

Permalink
feat: Create IIdeChannel services and register it into DI to be usabl…
Browse files Browse the repository at this point in the history
…e by add-ins
  • Loading branch information
dr1rrb committed Oct 9, 2024
1 parent 86dfaca commit 2099883
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 177 deletions.

This file was deleted.

117 changes: 107 additions & 10 deletions src/Uno.UI.RemoteControl.Host/IDEChannel/IdeChannelServer.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,129 @@
using System;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;
using Uno.UI.RemoteControl.Host.IDEChannel;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using StreamJsonRpc;
using Uno.Extensions;
using Uno.UI.RemoteControl.Messaging.IdeChannel;
using Uno.UI.RemoteControl.Services;

namespace Uno.UI.RemoteControl.Host.IdeChannel;

internal class IdeChannelServer : IIdeChannelServer
/// <summary>
/// The server end for the "ide-channel" communication.
/// </summary>
internal class IdeChannelServer : IIdeChannel, IDisposable
{
public IdeChannelServer()
private readonly ILogger _logger;
private readonly IConfiguration _configuration;

private readonly Task<bool> _initializeTask;
private NamedPipeServerStream? _pipeServer;
private JsonRpc? _rpcServer;
private Proxy? _proxy;

public IdeChannelServer(ILogger<IdeChannelServer> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;

_initializeTask = Task.Run(InitializeServer);
}

public event EventHandler<IdeMessage>? MessageFromIDE;

public event EventHandler<IdeMessageEnvelope>? MessageFromDevServer;
#region IIdeChannel

public async Task SendToIdeAsync(IdeMessage message)
/// <inheritdoc />
public event EventHandler<IdeMessage>? MessageFromIde;

/// <inheritdoc />
async Task IIdeChannel.SendToIdeAsync(IdeMessage message, CancellationToken ct)
{
MessageFromDevServer?.Invoke(this, IdeMessageSerializer.Serialize(message));
await WaitForReady(ct);

if (_proxy is null)
{
this.Log().Log(LogLevel.Information, "Received an message to send to the IDE, but there is no connection available for that.");
}
else
{
_proxy.SendToIde(message);
}

await Task.Yield();
}
#endregion

/// <inheritdoc />
public async ValueTask<bool> WaitForReady(CancellationToken ct = default)
#pragma warning disable VSTHRD003 // Avoid awaiting foreign Tasks
=> await _initializeTask;
#pragma warning restore VSTHRD003

public async Task SendToDevServerAsync(IdeMessageEnvelope message, CancellationToken ct)
/// <summary>
/// Initialize as dev-server (cf. IdeChannelClient for init as IDE)
/// </summary>
private async Task<bool> InitializeServer()
{
MessageFromIDE?.Invoke(this, IdeMessageSerializer.Deserialize(message));
if (!Guid.TryParse(_configuration["ideChannel"], out var ideChannel))
{
_logger.LogDebug("No IDE Channel ID specified, skipping.");
return false;
}

await Task.Yield();
_pipeServer = new NamedPipeServerStream(
pipeName: ideChannel.ToString(),
direction: PipeDirection.InOut,
maxNumberOfServerInstances: 1,
transmissionMode: PipeTransmissionMode.Byte,
options: PipeOptions.Asynchronous | PipeOptions.WriteThrough);

await _pipeServer.WaitForConnectionAsync();

if (_logger.IsEnabled(LogLevel.Debug))
{
_logger.LogDebug("IDE Connected");
}

_proxy = new(this);
_rpcServer = JsonRpc.Attach(_pipeServer, _proxy);

_ = StartKeepAliveAsync();
return true;
}

private async Task StartKeepAliveAsync()
{
while (_pipeServer?.IsConnected ?? false)
{
_proxy?.SendToIde(new KeepAliveIdeMessage("dev-server"));

await Task.Delay(5000);
}
}

/// <inheritdoc />
public void Dispose()
{
_rpcServer?.Dispose();
_pipeServer?.Dispose();
}

private class Proxy(IdeChannelServer Owner) : IIdeChannelServer
{
/// <inheritdoc />
public event EventHandler<IdeMessageEnvelope>? MessageFromDevServer;

/// <inheritdoc />
public Task SendToDevServerAsync(IdeMessageEnvelope envelope, CancellationToken ct)
{
Owner.MessageFromIde?.Invoke(Owner, IdeMessageSerializer.Deserialize(envelope));
return Task.CompletedTask;
}

internal void SendToIde(IdeMessage message)
=> MessageFromDevServer?.Invoke(this, IdeMessageSerializer.Serialize(message));
}
}

This file was deleted.

5 changes: 3 additions & 2 deletions src/Uno.UI.RemoteControl.Host/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Threading.Tasks;
using Uno.UI.RemoteControl.Host.Extensibility;
using Uno.UI.RemoteControl.Host.IdeChannel;
using Uno.UI.RemoteControl.Services;

namespace Uno.UI.RemoteControl.Host
{
Expand Down Expand Up @@ -77,7 +78,7 @@ static async Task Main(string[] args)
})
.ConfigureServices(services =>
{
services.AddSingleton<IIdeChannelServerProvider, IdeChannelServerProvider>();
services.AddSingleton<IIdeChannel, IdeChannelServer>();
});

if (solution is not null)
Expand All @@ -88,7 +89,7 @@ static async Task Main(string[] args)

var host = builder.Build();

host.Services.GetService<IIdeChannelServerProvider>();
host.Services.GetService<IIdeChannel>();

using var parentObserver = ParentProcessObserver.Observe(host, parentPID);

Expand Down
4 changes: 2 additions & 2 deletions src/Uno.UI.RemoteControl.Host/RemoteControlExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Uno.Extensions;
using Uno.UI.RemoteControl.Host.IdeChannel;
using Uno.UI.RemoteControl.Services;

namespace Uno.UI.RemoteControl.Host
{
Expand Down Expand Up @@ -37,7 +37,7 @@ public static IApplicationBuilder UseRemoteControlServer(
{
using (var server = new RemoteControlServer(
configuration,
context.RequestServices.GetService<IIdeChannelServerProvider>() ?? throw new InvalidOperationException("IIDEChannelServerProvider is required"),
context.RequestServices.GetService<IIdeChannel>() ?? throw new InvalidOperationException("IIdeChannel is required"),
context.RequestServices))
{
await server.RunAsync(await context.WebSockets.AcceptWebSocketAsync(), CancellationToken.None);
Expand Down
41 changes: 12 additions & 29 deletions src/Uno.UI.RemoteControl.Host/RemoteControlServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using Uno.UI.RemoteControl.HotReload.Messages;
using Uno.UI.RemoteControl.Messages;
using Uno.UI.RemoteControl.Messaging.IdeChannel;
using Uno.UI.RemoteControl.Services;

namespace Uno.UI.RemoteControl.Host;

Expand All @@ -29,22 +30,23 @@ internal class RemoteControlServer : IRemoteControlServer, IDisposable
private readonly CancellationTokenSource _ct = new();

private WebSocket? _socket;
private IdeChannelServer? _ideChannelServer;
private readonly List<string> _appInstanceIds = new();
private readonly IConfiguration _configuration;
private readonly IIdeChannelServerProvider _ideChannelProvider;
private readonly IIdeChannel _ideChannel;
private readonly IServiceProvider _serviceProvider;

public RemoteControlServer(IConfiguration configuration, IIdeChannelServerProvider ideChannelProvider, IServiceProvider serviceProvider)
public RemoteControlServer(IConfiguration configuration, IIdeChannel ideChannel, IServiceProvider serviceProvider)
{
_configuration = configuration;
_ideChannelProvider = ideChannelProvider;
_ideChannel = ideChannel;
_serviceProvider = serviceProvider;

if (this.Log().IsEnabled(LogLevel.Debug))
{
this.Log().LogDebug("Starting RemoteControlServer");
}

_ideChannel.MessageFromIde += ProcessIdeMessage;
}

string IRemoteControlServer.GetServerConfiguration(string key)
Expand Down Expand Up @@ -131,13 +133,14 @@ private AssemblyLoadContext GetAssemblyLoadContext(string applicationId)
private void RegisterProcessor(IServerProcessor hotReloadProcessor)
=> _processors[hotReloadProcessor.Scope] = hotReloadProcessor;

public IdeChannelServer? IDEChannelServer => _ideChannelServer;

public async Task RunAsync(WebSocket socket, CancellationToken ct)
{
_socket = socket;

await TryStartIDEChannelAsync();
if (_ideChannel is IdeChannelServer srv)
{
await srv.WaitForReady(ct);
}

while (await WebSocketHelper.ReadFrame(socket, ct) is Frame frame)
{
Expand Down Expand Up @@ -186,21 +189,6 @@ public async Task RunAsync(WebSocket socket, CancellationToken ct)
}
}

private async Task TryStartIDEChannelAsync()
{
if (_ideChannelServer is { } oldChannel)
{
oldChannel.MessageFromIDE -= ProcessIdeMessage;
}

_ideChannelServer = await _ideChannelProvider.GetIdeChannelServerAsync();

if (_ideChannelServer is { } newChannel)
{
newChannel.MessageFromIDE += ProcessIdeMessage;
}
}

private void ProcessIdeMessage(object? sender, IdeMessage message)
{
if (_processors.TryGetValue(message.Scope, out var processor))
Expand Down Expand Up @@ -442,13 +430,8 @@ await WebSocketHelper.SendFrame(
}
}

public async Task SendMessageToIDEAsync(IdeMessage message)
{
if (IDEChannelServer is not null)
{
await IDEChannelServer.SendToIdeAsync(message);
}
}
public Task SendMessageToIDEAsync(IdeMessage message)
=> _ideChannel.SendToIdeAsync(message, default);

public void Dispose()
{
Expand Down
16 changes: 0 additions & 16 deletions src/Uno.UI.RemoteControl.Messaging/IDEChannel/IIdeChannelServer.cs

This file was deleted.

Loading

0 comments on commit 2099883

Please sign in to comment.