mirror of
https://github.com/scratchfoundation/scratch-link.git
synced 2025-08-28 22:39:42 -04:00
VERY rough draft for SessionManager
This commit is contained in:
parent
4441a72551
commit
75f7caace5
10 changed files with 179 additions and 37 deletions
|
@ -10,10 +10,10 @@ namespace ScratchLink;
|
|||
public static class MauiProgram
|
||||
{
|
||||
/// <summary>
|
||||
/// Build and return a MauiApp instance to host our app.
|
||||
/// Create and return a MauiAppBuilder which will build an instance to host our app.
|
||||
/// </summary>
|
||||
/// <returns>A new instance of <see cref="MauiApp"/> configured for our app.</returns>
|
||||
public static MauiApp CreateMauiApp()
|
||||
public static MauiAppBuilder CreateMauiAppBuilder()
|
||||
{
|
||||
var builder = MauiApp.CreateBuilder();
|
||||
builder
|
||||
|
@ -23,6 +23,6 @@ public static class MauiProgram
|
|||
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
|
||||
});
|
||||
|
||||
return builder.Build();
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
namespace ScratchLink;
|
||||
|
||||
using Foundation;
|
||||
using ScratchLink.Platforms.MacCatalyst;
|
||||
|
||||
/// <summary>
|
||||
/// The AppDelegate connects UIApplication to MauiApp on MacCatalyst.
|
||||
|
@ -17,5 +18,10 @@ public class AppDelegate : MauiUIApplicationDelegate
|
|||
/// MacCatalyst-specific configuration can go here.
|
||||
/// </summary>
|
||||
/// <returns>A new instance of <see cref="MauiApp"/> configured for our app.</returns>
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
protected override MauiApp CreateMauiApp()
|
||||
{
|
||||
var builder = MauiProgram.CreateMauiAppBuilder();
|
||||
builder.Services.Add(new ServiceDescriptor(typeof(SessionManager), typeof(MacSessionManager), ServiceLifetime.Singleton));
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
|
|
19
scratch-link/Platforms/MacCatalyst/MacSessionManager.cs
Normal file
19
scratch-link/Platforms/MacCatalyst/MacSessionManager.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
// <copyright file="MacSessionManager.cs" company="Scratch Foundation">
|
||||
// Copyright (c) Scratch Foundation. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace ScratchLink.Platforms.MacCatalyst;
|
||||
|
||||
using System.Net.WebSockets;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the Mac-specific functionality of the SessionManager.
|
||||
/// </summary>
|
||||
internal class MacSessionManager : SessionManager
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
protected override Session MakeNewSession(WebSocketContext webSocketContext)
|
||||
{
|
||||
return new Session(webSocketContext);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@
|
|||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
namespace ScratchLink.WinUI;
|
||||
|
||||
using ScratchLink.Platforms.Windows;
|
||||
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
|
@ -25,5 +27,10 @@ public partial class App : MauiWinUIApplication
|
|||
/// MacCatalyst-specific configuration can go here.
|
||||
/// </summary>
|
||||
/// <returns>A new instance of <see cref="MauiApp"/> configured for our app.</returns>
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
protected override MauiApp CreateMauiApp()
|
||||
{
|
||||
var builder = MauiProgram.CreateMauiAppBuilder();
|
||||
builder.Services.Add(new ServiceDescriptor(typeof(SessionManager), typeof(WindowsSessionManager), ServiceLifetime.Singleton));
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
|
|
19
scratch-link/Platforms/Windows/WindowsSessionManager.cs
Normal file
19
scratch-link/Platforms/Windows/WindowsSessionManager.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
// <copyright file="WindowsSessionManager.cs" company="Scratch Foundation">
|
||||
// Copyright (c) Scratch Foundation. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace ScratchLink.Platforms.Windows;
|
||||
|
||||
using System.Net.WebSockets;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the Windows-specific functionality of the SessionManager.
|
||||
/// </summary>
|
||||
internal class WindowsSessionManager : SessionManager
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
protected override Session MakeNewSession(WebSocketContext webSocketContext)
|
||||
{
|
||||
return new Session(webSocketContext);
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ public partial class ScratchLinkApp : Application
|
|||
{
|
||||
private const int WebSocketPort = 20111;
|
||||
|
||||
private readonly SessionManager sessionManager;
|
||||
private readonly WebSocketListener webSocketListener;
|
||||
|
||||
/// <summary>
|
||||
|
@ -38,22 +39,27 @@ public partial class ScratchLinkApp : Application
|
|||
return;
|
||||
}
|
||||
|
||||
this.sessionManager = IPlatformApplication.Current.Services.GetService<SessionManager>();
|
||||
|
||||
this.webSocketListener = new ()
|
||||
{
|
||||
Handlers =
|
||||
OnWebSocketConnection = (webSocketContext) =>
|
||||
{
|
||||
{ ServicePath.BLE, this.HandleSessionDebug },
|
||||
{ ServicePath.BT, this.HandleSessionDebug },
|
||||
this.sessionManager.ClientDidConnect(webSocketContext);
|
||||
},
|
||||
OnOtherConnection = (context) =>
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
},
|
||||
};
|
||||
this.webSocketListener.Listen(new[]
|
||||
this.webSocketListener.Start(new[]
|
||||
{
|
||||
string.Format("http://127.0.0.1:{0}/", WebSocketPort),
|
||||
string.Format("http://localhost:{0}/", WebSocketPort),
|
||||
});
|
||||
}
|
||||
|
||||
private void HandleSessionDebug(HttpListenerWebSocketContext context)
|
||||
private void HandleSessionDebug(WebSocketContext context)
|
||||
{
|
||||
var origin = context.Headers.Get("origin");
|
||||
var socket = context.WebSocket;
|
||||
|
@ -63,10 +69,4 @@ public partial class ScratchLinkApp : Application
|
|||
});
|
||||
socket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
|
||||
}
|
||||
|
||||
private static class ServicePath
|
||||
{
|
||||
public const string BLE = "/scratch/ble";
|
||||
public const string BT = "/scratch/bt";
|
||||
}
|
||||
}
|
||||
|
|
23
scratch-link/Session.cs
Normal file
23
scratch-link/Session.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
// <copyright file="Session.cs" company="Scratch Foundation">
|
||||
// Copyright (c) Scratch Foundation. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace ScratchLink;
|
||||
|
||||
using System.Net.WebSockets;
|
||||
|
||||
internal class Session
|
||||
{
|
||||
private WebSocketContext context;
|
||||
|
||||
public Session(WebSocketContext context)
|
||||
{
|
||||
this.context = context;
|
||||
var webSocket = context.WebSocket;
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await webSocket.SendString("hello", true, CancellationToken.None);
|
||||
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
|
||||
});
|
||||
}
|
||||
}
|
39
scratch-link/SessionManager.cs
Normal file
39
scratch-link/SessionManager.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
// <copyright file="SessionManager.cs" company="Scratch Foundation">
|
||||
// Copyright (c) Scratch Foundation. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace ScratchLink;
|
||||
|
||||
using System.Net.WebSockets;
|
||||
|
||||
/// <summary>
|
||||
/// This class connects a WebSocket to the appropriate session type and tracks the collection of active sessions.
|
||||
/// </summary>
|
||||
internal abstract class SessionManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Activated when the number of active sessions changes.
|
||||
/// </summary>
|
||||
public event EventHandler ActiveSessionCountChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count of active connected WebSocket sessions.
|
||||
/// </summary>
|
||||
public int ActiveSessionCount { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Call this with a new connection context to ask the SessionManager to build and manage a session for it.
|
||||
/// </summary>
|
||||
/// <param name="webSocketContext">The WebSocket context which the SessionManager should adopt and connect to a session.</param>
|
||||
public void ClientDidConnect(WebSocketContext webSocketContext)
|
||||
{
|
||||
var session = this.MakeNewSession(webSocketContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new Session object to handle a new WebSocket connection.
|
||||
/// </summary>
|
||||
/// <param name="webSocketContext">Create a Session to handle this connection.</param>
|
||||
/// <returns>A new Session object connected to the provided context.</returns>
|
||||
protected abstract Session MakeNewSession(WebSocketContext webSocketContext);
|
||||
}
|
33
scratch-link/WebSocketExtensions.cs
Normal file
33
scratch-link/WebSocketExtensions.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
// <copyright file="WebSocketExtensions.cs" company="Scratch Foundation">
|
||||
// Copyright (c) Scratch Foundation. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace ScratchLink;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// This class contains custom extensions to System.Net.WebSockets.WebSocket.
|
||||
/// </summary>
|
||||
internal static class WebSocketExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends a string over the WebSocket connection asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="ws"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="endOfMessage"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
public static Task SendString(this WebSocket ws, string message, bool endOfMessage, CancellationToken cancellationToken)
|
||||
{
|
||||
var messageBytes = Encoding.UTF8.GetBytes(message);
|
||||
var buffer = new ArraySegment<byte>(messageBytes);
|
||||
return ws.SendAsync(buffer, WebSocketMessageType.Text, endOfMessage, cancellationToken);
|
||||
}
|
||||
}
|
|
@ -22,9 +22,14 @@ internal class WebSocketListener
|
|||
public static bool IsSupported => HttpListener.IsSupported;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mapping of path to handler.
|
||||
/// Gets or sets the action which will be called when the listener receives a WebSocket connection.
|
||||
/// </summary>
|
||||
public Dictionary<string, Action<HttpListenerWebSocketContext>> Handlers { get; } = new ();
|
||||
public Action<WebSocketContext> OnWebSocketConnection { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the action which will be called when the listener receives a non-WebSocket connection.
|
||||
/// </summary>
|
||||
public Action<HttpListenerContext> OnOtherConnection { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Start listening for connections. If already listening, stop and restart with the new prefix list.
|
||||
|
@ -37,7 +42,7 @@ internal class WebSocketListener
|
|||
/// https://127.0.0.1/
|
||||
/// </code></example>
|
||||
/// </param>
|
||||
public void Listen(IEnumerable<string> prefixes)
|
||||
public void Start(IEnumerable<string> prefixes)
|
||||
{
|
||||
if (this.listener.IsListening)
|
||||
{
|
||||
|
@ -58,32 +63,23 @@ internal class WebSocketListener
|
|||
var context = await this.listener.GetContextAsync();
|
||||
if (context.Request.IsWebSocketRequest)
|
||||
{
|
||||
this.HandleWebSocketRequest(context);
|
||||
var webSocketContext = await context.AcceptWebSocketAsync(null);
|
||||
this.OnWebSocketConnection(webSocketContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.HandleOtherRequest(context);
|
||||
this.OnOtherConnection(context);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async void HandleWebSocketRequest(HttpListenerContext context)
|
||||
/// <summary>
|
||||
/// Stop listening for connections and terminate processing of all ongoing requests.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
var webSocket = await context.AcceptWebSocketAsync(null);
|
||||
var handler = this.Handlers[context.Request.Url.AbsolutePath];
|
||||
if (handler != null)
|
||||
{
|
||||
handler(webSocket);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleOtherRequest(HttpListenerContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
this.cts.Cancel();
|
||||
this.listener.Stop();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue