Skip to content

Commit

Permalink
feat: Add support for devserver IDE channel
Browse files Browse the repository at this point in the history
  • Loading branch information
jeromelaban committed Jan 8, 2024
1 parent 61e5886 commit dcecc26
Show file tree
Hide file tree
Showing 18 changed files with 915 additions and 552 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Threading.Tasks;
using Uno.UI.RemoteControl.Messaging.IdeChannel;

namespace Uno.UI.RemoteControl.Host.IdeChannel;

internal interface IIdeChannelServerProvider
{
Task<IdeChannelServer?> GetIdeChannelServerAsync();
}
33 changes: 33 additions & 0 deletions src/Uno.UI.RemoteControl.Host/IDEChannel/IdeChannelServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Threading.Tasks;
using Uno.UI.RemoteControl.Messaging.IdeChannel;

namespace Uno.UI.RemoteControl.Host.IdeChannel;

internal class IdeChannelServer : IIdeChannelServer
{
private IServiceProvider _serviceProvider;

public IdeChannelServer(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}

public event EventHandler<IdeMessage>? MessageFromIDE;

public event EventHandler<IdeMessage>? MessageFromDevServer;

public async Task SendToIdeAsync(IdeMessage message)
{
MessageFromDevServer?.Invoke(this, message);

await Task.Yield();
}

public async Task SendToDevServerAsync(IdeMessage message)
{
MessageFromIDE?.Invoke(this, message);

await Task.Yield();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System;
using System.IO.Pipes;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using StreamJsonRpc;
using Uno.UI.RemoteControl.Messaging.IdeChannel;

namespace Uno.UI.RemoteControl.Host.IdeChannel;

internal class IdeChannelServerProvider : IIdeChannelServerProvider
{
private readonly ILogger _logger;
private readonly IConfiguration _configuration;
private readonly IServiceProvider _serviceProvider;
private readonly Task<IdeChannelServer?> _initializeTask;
private NamedPipeServerStream? _pipeServer;
private IdeChannelServer? _ideChannelServer;
private JsonRpc? _rpcServer;

public IdeChannelServerProvider(ILogger<IdeChannelServerProvider> logger, IConfiguration configuration, IServiceProvider serviceProvider)
{
_logger = logger;
_configuration = configuration;
_serviceProvider = serviceProvider;

_initializeTask = Task.Run(Initialize);
}

private async Task<IdeChannelServer?> Initialize()
{
if (!Guid.TryParse(_configuration["ideChannel"], out var ideChannel))
{
_logger.LogDebug("No IDE Channel ID specified, skipping");
return null;
}

_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");
}

_ideChannelServer = new IdeChannelServer(_serviceProvider);
_ideChannelServer.MessageFromIDE += OnMessageFromIDE;
_rpcServer = JsonRpc.Attach(_pipeServer, _ideChannelServer);

_ = StartKeepaliveAsync();

return _ideChannelServer;
}

private async Task StartKeepaliveAsync()
{
while (_pipeServer?.IsConnected ?? false)
{
_ideChannelServer?.SendToIdeAsync(new KeepAliveIdeMessage());

await Task.Delay(5000);
}
}

private void OnMessageFromIDE(object? sender, IdeMessage ideMessage)
{
if (ideMessage is KeepAliveIdeMessage)
{
#if DEBUG
_logger.LogDebug("Keepalive from IDE");
#endif
}
else
{
_logger.LogDebug($"Unknown message type {ideMessage?.GetType()} from IDE");
}
}

public async Task<IdeChannelServer?> GetIdeChannelServerAsync()
{
#pragma warning disable IDE0022 // Use expression body for method
#pragma warning disable VSTHRD003 // Avoid awaiting foreign Tasks
return await _initializeTask;
#pragma warning restore VSTHRD003 // Avoid awaiting foreign Tasks
#pragma warning restore IDE0022 // Use expression body for method
}
}
22 changes: 17 additions & 5 deletions src/Uno.UI.RemoteControl.Host/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Mono.Options;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading.Tasks;
using Uno.UI.RemoteControl.Host.IdeChannel;

namespace Uno.UI.RemoteControl.Host
{
class Program
{
static void Main(string[] args)
static async Task Main(string[] args)
{
var httpPort = 0;
var parentPID = 0;
Expand All @@ -31,7 +36,7 @@ static void Main(string[] args)
throw new ArgumentException($"The parent process id parameter is invalid {s}");
}
}
},
}
};

p.Parse(args);
Expand All @@ -41,7 +46,7 @@ static void Main(string[] args)
throw new ArgumentException($"The httpPort parameter is required.");
}

var host = new WebHostBuilder()
var builder = new WebHostBuilder()
.UseSetting("UseIISIntegration", false.ToString())
.UseKestrel()
.UseUrls($"http://*:{httpPort}/")
Expand All @@ -56,11 +61,18 @@ static void Main(string[] args)
{
config.AddCommandLine(args);
})
.Build();
.ConfigureServices(services =>
{
services.AddSingleton<IIdeChannelServerProvider, IdeChannelServerProvider>();
});

var host = builder.Build();

host.Services.GetService<IIdeChannelServerProvider>();

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

host.Run();
await host.RunAsync();
}
}
}
11 changes: 8 additions & 3 deletions src/Uno.UI.RemoteControl.Host/RemoteControlExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.Threading;
using System;
using System.Threading;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Uno.Extensions;
using Uno.UI.RemoteControl.Host.IdeChannel;

namespace Uno.UI.RemoteControl.Host
{
Expand Down Expand Up @@ -33,9 +35,12 @@ public static IApplicationBuilder UseRemoteControlServer(
{
if (context.RequestServices.GetService<IConfiguration>() is { } configuration)
{
using (var server = new RemoteControlServer(configuration))
using (var server = new RemoteControlServer(
configuration,
context.RequestServices.GetService<IIdeChannelServerProvider>() ?? throw new InvalidOperationException("IIDEChannelServerProvider is required"),
context.RequestServices))
{
await server.Run(await context.WebSockets.AcceptWebSocketAsync(), CancellationToken.None);
await server.RunAsync(await context.WebSockets.AcceptWebSocketAsync(), CancellationToken.None);
}
}
else
Expand Down
Loading

0 comments on commit dcecc26

Please sign in to comment.