diff --git a/.gitignore b/.gitignore index 45d2095cdb31cf0937596e088eea358648b4bccd..d499d77000223514ac4d47d5200a27b35f00b034 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ server/worldstorage/README.md # all generated directories wwwroot/ +!wwwroot/ws.html Attributes/ Authentication/ Converters/ diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/WebSocketController.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/WebSocketController.cs new file mode 100644 index 0000000000000000000000000000000000000000..6e80b54d679b0ac6e03429de8246451ca5e6ab9d --- /dev/null +++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/WebSocketController.cs @@ -0,0 +1,125 @@ +// +// ARF - Augmented Reality Framework (ETSI ISG ARF) +// +// Copyright 2024 ETSI +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Last change: June 2024 +// + +using System; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Net.WebSockets; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http; +using Swashbuckle.AspNetCore.Annotations; +using Swashbuckle.AspNetCore.SwaggerGen; + +#pragma warning disable CS1591 // Fehlendes XML-Kommentar für öffentlich sichtbaren Typ oder Element +namespace ETSI.ARF.OpenAPI.WorldStorage.Services +{ + // + // ETSI-ARF World Analysis WebSocket implementation + // see also: https://learn.microsoft.com/de-de/aspnet/core/fundamentals/websockets?view=aspnetcore-5.0 + // + public class WebSocketController : ControllerBase + { + [HttpGet("/ws")] + public async Task Get() + { + if (HttpContext.WebSockets.IsWebSocketRequest) + { + using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); + + if (webSocket.State == WebSocketState.Open) + { + // Response an OK message + await SendText(webSocket, "Hello, here is the ARF World Analysis services!"); + } + await Echo(HttpContext, webSocket); + } + else + { + await HttpContext.Response.WriteAsync("ETSI-ARF World Analysis: Not a valid WebSocket request."); + HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest; + } + } + + // + // Send a line of text + // + private async Task SendText(WebSocket webSocket, string text) + { + // Response an OK message + var message = text; + var bytes = Encoding.UTF8.GetBytes(message); + var arraySegment = new ArraySegment<byte>(bytes, 0, bytes.Length); + await webSocket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None); + } + + // + // Send the time all seconds + // + private async Task SendTime(WebSocket webSocket) + { + while (true) + { + var message = "Hello, my time is: " + DateTime.Now.ToLocalTime(); + var bytes = Encoding.UTF8.GetBytes(message); + var arraySegment = new ArraySegment<byte>(bytes, 0, bytes.Length); + + if (webSocket.State == WebSocketState.Open) + { + await webSocket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None); + } + else if (webSocket.State == WebSocketState.Closed || webSocket.State == WebSocketState.Aborted) + { + break; + } + Thread.Sleep(1000); + } + } + + // + // Echo incoming messages + // + private async Task Echo(HttpContext context, WebSocket webSocket) + { + var buffer = new byte[1024 * 4]; + + // get the first data block + WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); + while (!result.CloseStatus.HasValue) + { + // test + await SendText(webSocket, "Thanks, I got this message:"); + + // echo the message back to the client + await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); + + // get the next block + result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); + } + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + } + } +} +#pragma warning restore CS1591 // Fehlendes XML-Kommentar für öffentlich sichtbaren Typ oder Element + + diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs index d9d13074bbe7b0a5ba668b2275b377bff9d421d3..48be55250c2022d50514a393758b36234e0102d7 100644 --- a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs +++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs @@ -161,6 +161,15 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis app.UseHsts(); } + // ETSI-ARF Websocket implementation + var webSocketOptions = new WebSocketOptions() + { + KeepAliveInterval = TimeSpan.FromSeconds(120), + }; + //webSocketOptions.AllowedOrigins.Add("https://etsi.hhi.fraunhofer.de"); + //webSocketOptions.AllowedOrigins.Add("https://www.client.com"); + app.UseWebSockets(); + app.UseHttpsRedirection(); app.UseDefaultFiles(); app.UseStaticFiles();