public async Task InvokeAsync(HttpContext ctx, AnperiDbContext dbContext) { if (ctx.Request.Path != _options.Value.Path) { await _next(ctx); return; } if (!ctx.WebSockets.IsWebSocketRequest) { ctx.Response.StatusCode = StatusCodes.Status400BadRequest; return; } try { await HandleWebSocket(ctx, dbContext); } catch (WebSocketException se) { if (!(se.InnerException is BadHttpRequestException)) { throw; } _logger.LogWarning( $"BadHttpRequestException occured while handling a websocket: {se.Message} -> {se.InnerException.Message}"); } }
private async Task <List <AuthenticatedWebSocketConnection> > GetOnlinePairedDevices( RegisteredDevice device, AnperiDbContext dbContext) { IEnumerable <int> pairedDeviceIds = await GetPairedDeviceIds(device, dbContext); lock (_activeConnections) { return(_activeConnections.Where(c => pairedDeviceIds.Contains(c.Key)).Select(c => c.Value).ToList()); } }
public AuthenticatedWebSocketConnection(HttpContext context, WebSocket socket, byte[] buffer, RegisteredDevice device, AnperiDbContext dbContext, ILogger <AnperiWebSocketMiddleware> logger, AnperiWebSocketMiddleware anperiManager, List <AuthenticatedWebSocketConnection> onlinePairedDevices) { _context = context; _socket = socket; _buffer = buffer; _device = device; _logger = logger; _anperiManager = anperiManager; _db = dbContext; _loggedInPairedDevices = onlinePairedDevices ?? new List <AuthenticatedWebSocketConnection>(); }
private async Task HandleWebSocket(HttpContext ctx, AnperiDbContext dbContext) { WebSocket socket = await ctx.WebSockets.AcceptWebSocketAsync(); var buffer = new byte[_options.Value.WsBufferSize]; WebSocketApiResult apiObjectResult = await socket.ReceiveApiMessage(buffer, _options.Value.RequestCancelToken); bool authFailed = true; WebSocketCloseStatus closeStatus = WebSocketCloseStatus.Empty; if (apiObjectResult.Obj == null) { await socket.SendJson( SharedJsonApiObjectFactory.CreateError(apiObjectResult.JsonException.Message), _options.Value.RequestCancelToken); } else { if (apiObjectResult.Obj.context == JsonApiContextTypes.server && apiObjectResult.Obj.message_type == JsonApiMessageTypes.request) { apiObjectResult.Obj.data.TryGetValue(nameof(JsonLoginData.device_type), out string typeString); if (Enum.TryParse(typeString, out SharedJsonDeviceType type) && Enum.TryParse(apiObjectResult.Obj.message_code, out SharedJsonRequestCode code)) { RegisteredDevice device = null; switch (code) { case SharedJsonRequestCode.login: if (!apiObjectResult.Obj.data.TryGetValue("token", out string token)) { await socket.SendJson( SharedJsonApiObjectFactory.CreateError("Error retrieving token from request.")); } else { switch (type) { case SharedJsonDeviceType.host: device = dbContext.Hosts.SingleOrDefault(d => d.Token == token); break; case SharedJsonDeviceType.peripheral: device = dbContext.Peripherals.SingleOrDefault(d => d.Token == token); break; default: await socket.SendJson( SharedJsonApiObjectFactory.CreateError( "You need to be a host or peripheral.")); break; } if (device != null) { AuthenticatedWebSocketConnection activeConn; lock (_activeConnections) { _activeConnections.TryGetValue(device.Id, out activeConn); } if (activeConn != null) { activeConn.Abort(); await RemoveLoggedInDevice(activeConn, dbContext); } closeStatus = await LoginDevice(ctx, socket, buffer, device, dbContext); authFailed = false; } } break; case SharedJsonRequestCode.register: switch (type) { case SharedJsonDeviceType.host: device = new Host(); break; case SharedJsonDeviceType.peripheral: device = new Peripheral(); break; default: await socket.SendJson( SharedJsonApiObjectFactory.CreateError( "You need to be a host or peripheral.")); break; } if (device != null) { device.Token = Cryptography.CreateAuthToken(); if (apiObjectResult.Obj.data.TryGetValue("name", out string name)) { device.Name = name; } else { await socket.SendJson( SharedJsonApiObjectFactory.CreateError( "A device registration requires a name!")); } if (string.IsNullOrWhiteSpace(device.Name)) { device.Name = "devices want a name :("; } dbContext.RegisteredDevices.Add(device); await dbContext.SaveChangesAsync(); await socket.SendJson( SharedJsonApiObjectFactory.CreateRegisterResponse(device.Token, device.Name)); closeStatus = await LoginDevice(ctx, socket, buffer, device, dbContext); authFailed = false; } break; default: await socket.SendJson( SharedJsonApiObjectFactory.CreateError("Only login and register are valid here.")); break; } } else { await socket.SendJson( SharedJsonApiObjectFactory.CreateError( "Error parsing correct login or register parameters.")); } } } if (authFailed) { await socket.SendJson(SharedJsonApiObjectFactory.CreateLoginResponse(false, null)); CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(1000)); await socket.CloseAsync(WebSocketCloseStatus.PolicyViolation, "Authentication failed.", cts.Token); } else if (_options.Value.RequestCancelToken.IsCancellationRequested) { CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(1000)); await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Server is shutting down.", cts.Token); } else { await socket.CloseAsync(closeStatus, apiObjectResult.SocketResult.CloseStatusDescription, CancellationToken.None); } }
private async Task OnDeviceLoggedIn(AuthenticatedWebSocketConnection connection, AnperiDbContext dbContext) { IEnumerable <int> pairedDeviceIds = await GetPairedDeviceIds(connection.Device, dbContext); lock (_activeConnections) { foreach (int pairedDeviceId in pairedDeviceIds) { _activeConnections.TryGetValue(pairedDeviceId, out var c); c?.OnPairedDeviceLogin(this, new AuthenticatedWebSocketEventArgs(connection)); } } }
private async Task <IEnumerable <int> > GetPairedDeviceIds(RegisteredDevice device, AnperiDbContext dbContext) { return(await Task.Run(() => { IEnumerable <int> pairedDeviceIds; if (device is Host) { pairedDeviceIds = dbContext.HostPeripherals.Where(hp => hp.HostId == device.Id) .Select(hp => hp.PeripheralId); } else if (device is Peripheral) { pairedDeviceIds = dbContext.HostPeripherals.Where(hp => hp.PeripheralId == device.Id) .Select(hp => hp.HostId); } else { throw new NotImplementedException( $"The device type {device.GetType().AssemblyQualifiedName} is not implemented in GetPairedDeviceIds"); } return pairedDeviceIds.ToList(); })); }
private async Task RemoveLoggedInDevice(AuthenticatedWebSocketConnection connection, AnperiDbContext dbContext) { lock (_syncRootActiveConnections) { _activeConnections.Remove(connection.Device.Id); } await OnDeviceLoggedOut(connection, dbContext); }
private async Task <WebSocketCloseStatus> LoginDevice(HttpContext context, WebSocket socket, byte[] buffer, RegisteredDevice device, AnperiDbContext dbContext) { await socket.SendJson(SharedJsonApiObjectFactory.CreateLoginResponse(true, device.Name)); List <AuthenticatedWebSocketConnection> connectedPairedDevices = await GetOnlinePairedDevices(device, dbContext); var connection = new AuthenticatedWebSocketConnection(context, socket, buffer, device, dbContext, _logger, this, connectedPairedDevices); lock (_syncRootActiveConnections) { _activeConnections.Add(connection.Device.Id, connection); } WebSocketCloseStatus closeStatus; try { await OnDeviceLoggedIn(connection, dbContext); closeStatus = await connection.Run(_options.Value.RequestCancelToken); } catch (WebSocketException se) { if (!(se.InnerException is BadHttpRequestException)) { throw; } closeStatus = WebSocketCloseStatus.Empty; _logger.LogWarning( $"BadHttpRequestException occured while handling a websocket: {se.Message} -> {se.InnerException.Message}"); } finally { if (!connection.IsAborted) { await RemoveLoggedInDevice(connection, dbContext); } } return(closeStatus); }