Ooui-tws-port/Ooui.AspNetCore/WebSocketHandler.cs

134 lines
4.8 KiB
C#
Raw Normal View History

2017-11-10 05:00:15 +00:00
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
2018-04-16 00:40:48 +00:00
using System.IO;
using Microsoft.Extensions.Logging;
2017-11-10 05:00:15 +00:00
namespace Ooui.AspNetCore
{
public static class WebSocketHandler
{
public static string WebSocketPath { get; set; } = "/ooui.ws";
2018-02-21 23:40:12 +00:00
public static TimeSpan SessionTimeout { get; set; } = TimeSpan.FromMinutes (1);
2017-11-10 05:00:15 +00:00
2018-02-21 23:40:12 +00:00
static readonly ConcurrentDictionary<string, PendingSession> pendingSessions =
new ConcurrentDictionary<string, PendingSession> ();
2017-11-10 05:00:15 +00:00
2018-04-16 00:54:30 +00:00
public static string BeginSession (HttpContext context, Element element, bool disposeElementAfterSession, ILogger logger)
2017-11-10 05:00:15 +00:00
{
var id = Guid.NewGuid ().ToString ("N");
2018-02-21 23:40:12 +00:00
var s = new PendingSession {
2017-11-10 05:00:15 +00:00
Element = element,
2018-02-21 23:40:12 +00:00
CreateTimeUtc = DateTime.UtcNow,
2018-04-16 00:54:30 +00:00
DisposeElementAfterSession = disposeElementAfterSession,
Logger = logger,
2017-11-10 05:00:15 +00:00
};
2018-02-21 23:40:12 +00:00
if (!pendingSessions.TryAdd (id, s)) {
2017-11-10 05:00:15 +00:00
throw new Exception ("Failed to schedule pending session");
}
return id;
}
2018-04-16 00:54:30 +00:00
public static async Task HandleWebSocketRequestAsync (HttpContext context)
2017-11-10 05:00:15 +00:00
{
void BadRequest (string message)
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
context.Response.ContentType = "text/plain; charset=utf-8";
using (var sw = new System.IO.StreamWriter (context.Response.Body)) {
sw.WriteLine (message);
}
}
2017-11-10 05:00:15 +00:00
//
// Make sure we get a good ID
//
if (!context.Request.Query.TryGetValue ("id", out var idValues)) {
BadRequest ("Missing `id`");
2017-11-10 05:00:15 +00:00
return;
}
var id = idValues.LastOrDefault ();
2017-11-10 05:00:15 +00:00
if (id == null || id.Length != 32) {
BadRequest ("Invalid `id`");
2017-11-10 05:00:15 +00:00
return;
}
//
// Clear old sessions
2017-11-10 05:00:15 +00:00
//
2018-02-21 23:40:12 +00:00
var toClear = pendingSessions.Where (x => (DateTime.UtcNow - x.Value.CreateTimeUtc) > SessionTimeout).ToList ();
foreach (var c in toClear) {
2018-02-21 23:40:12 +00:00
pendingSessions.TryRemove (c.Key, out var _);
2017-11-10 05:00:15 +00:00
}
//
// Find the pending session
2017-11-10 05:00:15 +00:00
//
2018-02-21 23:40:12 +00:00
if (!pendingSessions.TryRemove (id, out var activeSession)) {
BadRequest ("Unknown `id`");
return;
}
//
// Set the element's dimensions
//
if (!context.Request.Query.TryGetValue ("w", out var wValues) || wValues.Count < 1) {
BadRequest ("Missing `w`");
return;
}
if (!context.Request.Query.TryGetValue ("h", out var hValues) || hValues.Count < 1) {
BadRequest ("Missing `h`");
2017-11-10 05:00:15 +00:00
return;
}
2017-12-09 23:34:48 +00:00
var icult = System.Globalization.CultureInfo.InvariantCulture;
if (!double.TryParse (wValues.Last (), System.Globalization.NumberStyles.Any, icult, out var w))
w = 640;
2017-12-09 23:34:48 +00:00
if (!double.TryParse (hValues.Last (), System.Globalization.NumberStyles.Any, icult, out var h))
h = 480;
2017-11-10 05:00:15 +00:00
//
// OK, Run
//
var token = CancellationToken.None;
2018-04-16 00:40:48 +00:00
System.Net.WebSockets.WebSocket webSocket = null;
void Error (string m, Exception e) => activeSession?.Logger?.LogWarning (e, m);
2018-04-16 00:40:48 +00:00
//
// Create a new session and let it handle everything from here
//
try {
webSocket = await context.WebSockets.AcceptWebSocketAsync ("ooui").ConfigureAwait (false);
var session = new Ooui.WebSocketSession (webSocket, activeSession.Element, activeSession.DisposeElementAfterSession, w, h, Error, token);
2018-04-16 00:40:48 +00:00
await session.RunAsync ().ConfigureAwait (false);
}
catch (System.Net.WebSockets.WebSocketException ex) when (ex.WebSocketErrorCode == System.Net.WebSockets.WebSocketError.ConnectionClosedPrematurely) {
// The remote party closed the WebSocket connection without completing the close handshake.
}
catch (Exception ex) {
context.Abort ();
2018-04-16 00:54:30 +00:00
activeSession?.Logger?.LogWarning (ex, "Web socket session failed");
2018-04-16 00:40:48 +00:00
}
finally {
webSocket?.Dispose ();
}
2017-11-10 05:00:15 +00:00
}
2018-02-21 23:40:12 +00:00
class PendingSession
2017-11-10 05:00:15 +00:00
{
public Element Element;
2018-02-21 23:40:12 +00:00
public DateTime CreateTimeUtc;
2018-04-16 00:54:30 +00:00
public bool DisposeElementAfterSession;
public ILogger Logger;
2017-11-10 05:00:15 +00:00
}
}
}