scratch-link/scratch-link-common/WebSocketListener.cs

90 lines
2.7 KiB
C#
Raw Normal View History

2022-04-11 15:38:02 -07:00
// <copyright file="WebSocketListener.cs" company="Scratch Foundation">
// Copyright (c) Scratch Foundation. All rights reserved.
// </copyright>
namespace ScratchLink;
2022-07-07 17:40:49 -07:00
using System;
using System.Collections.Generic;
2022-04-11 15:38:02 -07:00
using System.Net;
using System.Net.WebSockets;
2022-07-07 17:40:49 -07:00
using System.Threading;
using System.Threading.Tasks;
2022-04-11 15:38:02 -07:00
/// <summary>
/// Listen for WebSocket connections and direct them to service handlers.
/// </summary>
internal class WebSocketListener
{
private readonly HttpListener listener = new ();
2022-04-11 15:38:02 -07:00
private readonly CancellationTokenSource cts = new ();
2022-04-11 15:38:02 -07:00
/// <summary>
/// Gets a value indicating whether WebSocketListener is supported in the current environment.
/// </summary>
public static bool IsSupported => HttpListener.IsSupported;
/// <summary>
2022-04-12 14:22:05 -07:00
/// Gets or sets the action which will be called when the listener receives a WebSocket connection.
2022-04-11 15:38:02 -07:00
/// </summary>
2022-04-12 14:22:05 -07:00
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; }
2022-04-11 15:38:02 -07:00
/// <summary>
/// Start listening for connections. If already listening, stop and restart with the new prefix list.
/// </summary>
/// <param name="prefixes">
/// The list of HTTP(S) URL prefixes to listen on.
/// Use "http" instead of "ws" or "https" instead of "wss".
/// <example><code>
/// http://locahost:1234/
/// https://127.0.0.1/
/// </code></example>
/// </param>
2022-04-12 14:22:05 -07:00
public void Start(IEnumerable<string> prefixes)
2022-04-11 15:38:02 -07:00
{
if (this.listener.IsListening)
{
throw new InvalidOperationException();
}
foreach (var prefix in prefixes)
{
this.listener.Prefixes.Add(prefix);
}
this.listener.Start();
Task.Run(async () =>
{
CancellationToken token = this.cts.Token;
while (!token.IsCancellationRequested)
{
var context = await this.listener.GetContextAsync();
if (context.Request.IsWebSocketRequest)
{
2022-04-12 14:22:05 -07:00
var webSocketContext = await context.AcceptWebSocketAsync(null);
this.OnWebSocketConnection(webSocketContext);
2022-04-11 15:38:02 -07:00
}
else
{
2022-04-12 14:22:05 -07:00
this.OnOtherConnection(context);
2022-04-11 15:38:02 -07:00
}
}
});
}
2022-04-12 14:22:05 -07:00
/// <summary>
/// Stop listening for connections and terminate processing of all ongoing requests.
/// </summary>
public void Stop()
2022-04-11 15:38:02 -07:00
{
2022-04-12 14:22:05 -07:00
this.cts.Cancel();
this.listener.Stop();
2022-04-11 15:38:02 -07:00
}
}