diff --git a/API/GenHTTP.Api.csproj b/API/GenHTTP.Api.csproj index f6a0d771e..7c69508b5 100644 --- a/API/GenHTTP.Api.csproj +++ b/API/GenHTTP.Api.csproj @@ -6,9 +6,6 @@ GenHTTP HTTP Embedded Webserver Website Server Library C# Standard API README.md - - CS1591,CS1587,CS1572,CS1573,CS0612,CS0618 - diff --git a/API/Protocol/Connection.cs b/API/Protocol/Connection.cs index 92a0a2dfb..7720ae14b 100644 --- a/API/Protocol/Connection.cs +++ b/API/Protocol/Connection.cs @@ -18,13 +18,6 @@ public enum Connection /// the configured response and will close the connection after the response content /// has been sent. /// - Upgrade, - - /// - /// The connection is surrendered to the user logic and the server will not attempt - /// to close or change it in any way. - /// - [Obsolete("Required to allow Fleck to write their own response. Will be removed in GenHTTP 11.")] - UpgradeAndSurrender + Upgrade } diff --git a/API/Protocol/IRequest.cs b/API/Protocol/IRequest.cs index 5a1aed67a..3a4d05842 100644 --- a/API/Protocol/IRequest.cs +++ b/API/Protocol/IRequest.cs @@ -30,18 +30,6 @@ public interface IRequest : IDisposable /// The newly created response IResponseBuilder Respond(); - /// - /// Upgrades the connection of the client, causing the underlying socket and streams - /// to be exposed and to be used by another protocol, such as a websocket handler. - /// - /// The upgrade information to use - /// - /// After upgrading a connection, the server will surrender this socket and stream to your logic - /// to no further interaction is done by the framework with the client and is up to - /// your logic. - /// - UpgradeInfo Upgrade(); - #endregion #region General Infrastructure diff --git a/API/Protocol/IResponse.cs b/API/Protocol/IResponse.cs index 40e1e2173..1e5f5fdca 100644 --- a/API/Protocol/IResponse.cs +++ b/API/Protocol/IResponse.cs @@ -18,12 +18,6 @@ public interface IResponse : IDisposable /// Connection Connection { get; set; } - /// - /// Specifies whether the server should surrender the connection to the user code. - /// - [Obsolete("Replaced by ConnectionHandling.Upgrade. This property will be removed in GenHTTP 11.")] - public bool Upgraded { get; } - #endregion #region Headers diff --git a/API/Protocol/UpgradeInfo.cs b/API/Protocol/UpgradeInfo.cs deleted file mode 100644 index 6d21c76a1..000000000 --- a/API/Protocol/UpgradeInfo.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Net.Sockets; - -namespace GenHTTP.Api.Protocol; - -/// -/// Returned when upgrading a connection. -/// -/// The raw socket the current client is connected to -/// The underlying network stream used for the connection (already authenticated in case of TLS) -/// The response to return so that the server will ignore the connection further on -[Obsolete("Set Connection(Upgrade) instead and handle the stream within the response content. Will be removed in GenHTTP 11.")] -public record UpgradeInfo(Socket Socket, Stream Stream, IResponse Response); diff --git a/Adapters/AspNetCore/GenHTTP.Adapters.AspNetCore.csproj b/Adapters/AspNetCore/GenHTTP.Adapters.AspNetCore.csproj index 18cf015f1..2b878a728 100644 --- a/Adapters/AspNetCore/GenHTTP.Adapters.AspNetCore.csproj +++ b/Adapters/AspNetCore/GenHTTP.Adapters.AspNetCore.csproj @@ -8,9 +8,6 @@ Andreas Nägeli, Tanzim Hossain Romel - - CS1591,CS1587,CS1572,CS1573,CS0612,CS0618 - diff --git a/Adapters/AspNetCore/Types/Request.cs b/Adapters/AspNetCore/Types/Request.cs index bb8c0b153..8acd5faf7 100644 --- a/Adapters/AspNetCore/Types/Request.cs +++ b/Adapters/AspNetCore/Types/Request.cs @@ -102,8 +102,6 @@ public IResponseBuilder Respond() return _responseBuilder; } - public UpgradeInfo Upgrade() => throw new NotSupportedException("Web sockets are not supported by the Kestrel server implementation"); - internal void Configure(IServer server, HttpContext context) { var request = context.Request; diff --git a/Directory.Build.props b/Directory.Build.props index ae573d59d..527e8ef2e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,9 +9,9 @@ true enable - 10.5.0.0 - 10.5.0.0 - 10.5.0 + 11.0.0.0 + 11.0.0.0 + 11.0.0 Andreas Nägeli diff --git a/Engine/Internal/GenHTTP.Engine.Internal.csproj b/Engine/Internal/GenHTTP.Engine.Internal.csproj index c3dc1c388..30c6b0e6b 100644 --- a/Engine/Internal/GenHTTP.Engine.Internal.csproj +++ b/Engine/Internal/GenHTTP.Engine.Internal.csproj @@ -6,9 +6,6 @@ GenHTTP HTTP Embedded Webserver Server Library C# Standard Engine README.md - - CS1591,CS1587,CS1572,CS1573,CS0612,CS0618 - diff --git a/Engine/Internal/Protocol/ClientHandler.cs b/Engine/Internal/Protocol/ClientHandler.cs index f81d58586..d402db128 100644 --- a/Engine/Internal/Protocol/ClientHandler.cs +++ b/Engine/Internal/Protocol/ClientHandler.cs @@ -2,13 +2,17 @@ using System.IO.Pipelines; using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; + using GenHTTP.Api.Infrastructure; using GenHTTP.Api.Protocol; + using GenHTTP.Engine.Internal.Protocol.Parser; using GenHTTP.Engine.Internal.Utilities; using GenHTTP.Engine.Shared.Infrastructure; using GenHTTP.Engine.Shared.Types; + using Microsoft.Extensions.ObjectPool; + using StringContent = GenHTTP.Modules.IO.Strings.StringContent; namespace GenHTTP.Engine.Internal.Protocol; @@ -68,11 +72,9 @@ internal ClientHandler(Socket socket, PoolBufferedStream stream, X509Certificate internal async ValueTask Run() { - var status = Api.Protocol.Connection.Close; - try { - status = await HandlePipe(PipeReader.Create(Stream, ReaderOptions)).ConfigureAwait(false); + await HandlePipe(PipeReader.Create(Stream, ReaderOptions)).ConfigureAwait(false); } catch (Exception e) { @@ -80,34 +82,31 @@ internal async ValueTask Run() } finally { - if (status != Api.Protocol.Connection.UpgradeAndSurrender) + try { - try - { - await Stream.DisposeAsync(); - } - catch (Exception e) - { - Server.Companion?.OnServerError(ServerErrorScope.ClientConnection, Connection.GetAddress(), e); - } + await Stream.DisposeAsync(); + } + catch (Exception e) + { + Server.Companion?.OnServerError(ServerErrorScope.ClientConnection, Connection.GetAddress(), e); + } - try - { - Connection.Shutdown(SocketShutdown.Both); - await Connection.DisconnectAsync(false); - Connection.Close(); + try + { + Connection.Shutdown(SocketShutdown.Both); + await Connection.DisconnectAsync(false); + Connection.Close(); - Connection.Dispose(); - } - catch (Exception e) - { - Server.Companion?.OnServerError(ServerErrorScope.ClientConnection, Connection.GetAddress(), e); - } + Connection.Dispose(); + } + catch (Exception e) + { + Server.Companion?.OnServerError(ServerErrorScope.ClientConnection, Connection.GetAddress(), e); } } } - private async ValueTask HandlePipe(PipeReader reader) + private async ValueTask HandlePipe(PipeReader reader) { var context = ContextPool.Get(); @@ -139,9 +138,9 @@ private async ValueTask HandlePipe(PipeReader reader) var status = await HandleRequest(context.Request, !buffer.ReadRequired); - if (status is Api.Protocol.Connection.Close or Api.Protocol.Connection.UpgradeAndSurrender) + if (status is Api.Protocol.Connection.Close) { - return status; + return; } } } @@ -164,23 +163,16 @@ private async ValueTask HandlePipe(PipeReader reader) await reader.CompleteAsync(); } - - return Api.Protocol.Connection.Close; } private async ValueTask HandleRequest(Request request, bool dataRemaining) { - request.SetConnection(Server, Connection, Stream, EndPoint, Connection.GetAddress(), ClientCertificate); + request.SetConnection(Server, EndPoint, Connection.GetAddress(), ClientCertificate); var keepAliveRequested = request["Connection"]?.Equals("Keep-Alive", StringComparison.InvariantCultureIgnoreCase) ?? request.ProtocolType == HttpProtocol.Http11; var response = await Server.Handler.HandleAsync(request) ?? throw new InvalidOperationException("The root request handler did not return a response"); - if (response.Connection == Api.Protocol.Connection.UpgradeAndSurrender) - { - return Api.Protocol.Connection.UpgradeAndSurrender; - } - var closeRequested = response.Connection is Api.Protocol.Connection.Close or Api.Protocol.Connection.Upgrade; var active = await ResponseHandler.Handle(request, response, request.ProtocolType, keepAliveRequested && !closeRequested, dataRemaining); diff --git a/Engine/Internal/Protocol/Request.cs b/Engine/Internal/Protocol/Request.cs index c2cf93881..9af7d5b69 100644 --- a/Engine/Internal/Protocol/Request.cs +++ b/Engine/Internal/Protocol/Request.cs @@ -1,12 +1,9 @@ using System.Net; -using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; using GenHTTP.Api.Infrastructure; using GenHTTP.Api.Protocol; using GenHTTP.Api.Routing; - -using GenHTTP.Engine.Internal.Utilities; using GenHTTP.Engine.Shared.Types; using CookieCollection = GenHTTP.Engine.Shared.Types.CookieCollection; @@ -28,9 +25,6 @@ internal sealed class Request : IRequest private IClientConnection? _clientConnection; private IClientConnection? _localClient; - private Socket? _socket; - private Stream? _stream; - private FlexibleRequestMethod? _method; private RoutingTarget? _target; @@ -130,27 +124,15 @@ public IResponseBuilder Respond() return _responseBuilder; } - public UpgradeInfo Upgrade() - { - if (_socket == null || _stream == null) - { - throw new InvalidOperationException("Request is not initialized yet"); - } - - return new(_socket, _stream, new Response { Connection = Connection.UpgradeAndSurrender}); - } - public bool ContainsMultipleHeaders(string key) => _headers.ContainsMultiple(key); #endregion #region Parsing - internal void SetConnection(IServer server, Socket connection, PoolBufferedStream stream, IEndPoint endPoint, IPAddress? address, X509Certificate? clientCertificate) + internal void SetConnection(IServer server, IEndPoint endPoint, IPAddress? address, X509Certificate? clientCertificate) { _server = server; - _socket = connection; - _stream = stream; _endPoint = endPoint; var protocol = _endPoint.Secure ? ClientProtocol.Https : ClientProtocol.Http; diff --git a/Engine/Shared/GenHTTP.Engine.Shared.csproj b/Engine/Shared/GenHTTP.Engine.Shared.csproj index 91b14122e..2a025ecf8 100644 --- a/Engine/Shared/GenHTTP.Engine.Shared.csproj +++ b/Engine/Shared/GenHTTP.Engine.Shared.csproj @@ -6,9 +6,6 @@ GenHTTP Engine README.md - - CS1591,CS1587,CS1572,CS1573,CS0612,CS0618 - diff --git a/Engine/Shared/Types/Response.cs b/Engine/Shared/Types/Response.cs index 2664192f7..5a101e962 100644 --- a/Engine/Shared/Types/Response.cs +++ b/Engine/Shared/Types/Response.cs @@ -28,8 +28,6 @@ public Response() public Connection Connection { get; set; } - public bool Upgraded => Connection == Connection.UpgradeAndSurrender; - public DateTime? Expires { get; set; } public DateTime? Modified { get; set; } diff --git a/Modules/Compression/GenHTTP.Modules.Compression.csproj b/Modules/Compression/GenHTTP.Modules.Compression.csproj index f2a27b692..c77a59a00 100644 --- a/Modules/Compression/GenHTTP.Modules.Compression.csproj +++ b/Modules/Compression/GenHTTP.Modules.Compression.csproj @@ -8,9 +8,6 @@ GenHTTP HTTP Webserver C# Module Compression Brotli Gzip README.md - - CS1591,CS1587,CS1572,CS1573,CS0612,CS0618 - diff --git a/Modules/Compression/Providers/WrappedRequest.cs b/Modules/Compression/Providers/WrappedRequest.cs index 9bac17024..7b5e0e079 100644 --- a/Modules/Compression/Providers/WrappedRequest.cs +++ b/Modules/Compression/Providers/WrappedRequest.cs @@ -71,8 +71,6 @@ public WrappedRequest(IRequest inner, Stream decompressedContent) public IResponseBuilder Respond() => _inner.Respond(); - public UpgradeInfo Upgrade() => _inner.Upgrade(); - public void Dispose() { _decompressedContent.Dispose(); diff --git a/Modules/IO/Tracking/ChangeTrackingResource.cs b/Modules/IO/Tracking/ChangeTrackingResource.cs index 23f53a903..4f0dbf088 100644 --- a/Modules/IO/Tracking/ChangeTrackingResource.cs +++ b/Modules/IO/Tracking/ChangeTrackingResource.cs @@ -56,14 +56,6 @@ public async ValueTask WriteAsync(Stream target, uint bufferSize) /// True if the content has changed, false otherwise public async ValueTask CheckChangedAsync() => await CalculateChecksumAsync() != _lastChecksum; - /// - /// True, if the content of the resource has changed - /// since has been called - /// the last time. - /// - [Obsolete("Use CheckChangedAsync() instead. This method will be removed in GenHTTP 11.")] - public ValueTask HasChanged() => CheckChangedAsync(); - #endregion } diff --git a/Modules/Inspection/Concern/InspectionConcern.cs b/Modules/Inspection/Concern/InspectionConcern.cs index d21b2d359..7e326a609 100644 --- a/Modules/Inspection/Concern/InspectionConcern.cs +++ b/Modules/Inspection/Concern/InspectionConcern.cs @@ -88,7 +88,6 @@ public InspectionConcern(IHandler content, SerializationRegistry serialization) }, Response = (content != null) ? new { Status = content.Status.RawStatus, - Upgraded = content.Upgraded, Expires = content.Expires, Modified = content.Modified, Headers = content.Headers, diff --git a/Modules/Inspection/GenHTTP.Modules.Inspection.csproj b/Modules/Inspection/GenHTTP.Modules.Inspection.csproj index d20d64541..742075162 100644 --- a/Modules/Inspection/GenHTTP.Modules.Inspection.csproj +++ b/Modules/Inspection/GenHTTP.Modules.Inspection.csproj @@ -6,9 +6,6 @@ GenHTTP HTTP Webserver C# Module Debugging Debug Inspection Inspector README.md - - CS1591,CS1587,CS1572,CS1573,CS0612,CS0618 - diff --git a/Modules/Websockets/GenHTTP.Modules.Websockets.csproj b/Modules/Websockets/GenHTTP.Modules.Websockets.csproj index 6f5043878..c5a523810 100644 --- a/Modules/Websockets/GenHTTP.Modules.Websockets.csproj +++ b/Modules/Websockets/GenHTTP.Modules.Websockets.csproj @@ -8,26 +8,22 @@ GenHTTP HTTP Webserver C# Module Websockets README.md - - CS1591,CS1587,CS1572,CS1573,CS0612,CS0618 - - + - - - - - + + - + + + diff --git a/Modules/Websockets/IWebsocketConnection.cs b/Modules/Websockets/IWebsocketConnection.cs deleted file mode 100644 index 5a174d358..000000000 --- a/Modules/Websockets/IWebsocketConnection.cs +++ /dev/null @@ -1,59 +0,0 @@ -using GenHTTP.Api.Protocol; - -namespace GenHTTP.Modules.Websockets; - -/// -/// Represents a connected websocket client. -/// -public interface IWebsocketConnection -{ - - /// - /// The original request the websocket connection was created from. - /// - IRequest Request { get; } - - /// - /// Returns true if the socket is connected and can receive messages. - /// - bool IsAvailable { get; } - - /// - /// Sends a text message to the connected client. - /// - /// The message to be sent - Task SendAsync(string message); - - /// - /// Sends a binary message to the connected client. - /// - /// The message to be sent - Task SendAsync(byte[] message); - - /// - /// Sends a ping message to the connected client. - /// - /// The message to be sent - Task SendPingAsync(byte[] message); - - /// - /// Sends a pong message to the connected client. - /// - /// The message to be sent - Task SendPongAsync(byte[] message); - - /// - /// Gracefully closes the connection to the client. - /// - void Close(); - - /// - /// Closes the connection with the specified code. - /// - /// The code to be sent to the connected client - /// - /// See . - /// - void Close(int code); - -} diff --git a/Modules/Websockets/Legacy/Extensions.cs b/Modules/Websockets/Legacy/Extensions.cs deleted file mode 100644 index a7763621b..000000000 --- a/Modules/Websockets/Legacy/Extensions.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Fleck; -using GenHTTP.Api.Protocol; - -namespace GenHTTP.Modules.Websockets.Legacy; - -internal static class Extensions -{ - - internal static WebSocketHttpRequest Map(this IRequest request) - { - string? body = null; - - if (request.Content != null) - { - using var reader = new StreamReader(request.Content); - - body = reader.ReadToEnd(); - } - - var mapped = new WebSocketHttpRequest - { - Method = request.Method.RawMethod, - Path = request.Target.Path.ToString(true), - Scheme = request.EndPoint.Secure ? "wss" : "ws", - Bytes = [], - Body = body ?? string.Empty - }; - - foreach (var (k, v) in request.Headers) - { - mapped.Headers.Add(k, v); - } - - return mapped; - } - -} diff --git a/Modules/Websockets/Legacy/WebsocketConnection.cs b/Modules/Websockets/Legacy/WebsocketConnection.cs deleted file mode 100644 index 2c9ec7aba..000000000 --- a/Modules/Websockets/Legacy/WebsocketConnection.cs +++ /dev/null @@ -1,261 +0,0 @@ -using Fleck; -using GenHTTP.Api.Protocol; - -namespace GenHTTP.Modules.Websockets.Legacy; - -public sealed class WebsocketConnection : IWebSocketConnection, IWebsocketConnection -{ - private const int ReadSize = 1024 * 4; - - private bool _closing; - - private bool _closed; - - private Task? _readingTask; - - #region Get-/Setters - - public ISocket Socket { get; } - - public IHandler? Handler { get; private set; } - - public IRequest Request { get; } - - public IWebSocketConnectionInfo? ConnectionInfo { get; private set; } - - public Action OnOpen { get; set; } - - public Action OnClose { get; set; } - - public Action OnMessage { get; set; } - - public Action OnBinary { get; set; } - - public Action OnPing { get; set; } - - public Action OnPong { get; set; } - - public Action OnError { get; set; } - - public List SupportedProtocols { get; } - - public bool IsAvailable => !_closing && !_closed && Socket.Connected; - - #endregion - - #region Initialization - - public WebsocketConnection(ISocket socket, IRequest request, List supportedProtocols, - Func? onOpen, - Func? onClose, - Func? onMessage, - Func? onBinary, - Func? onPing, - Func? onPong, - Func? onError) - { - Socket = socket; - Request = request; - - SupportedProtocols = supportedProtocols; - - OnOpen = (onOpen != null) ? () => WebsocketDispatcher.Schedule(() => onOpen(this)) : () => { }; - OnClose = (onClose != null) ? () => WebsocketDispatcher.Schedule(() => onClose(this)) : () => { }; - OnMessage = (onMessage != null) ? x => WebsocketDispatcher.Schedule(() => onMessage(this, x)) : x => { }; - OnBinary = (onBinary != null) ? x => WebsocketDispatcher.Schedule(() => onBinary(this, x)) : x => { }; - OnPing = (onPing != null) ? x => WebsocketDispatcher.Schedule(() => onPing(this, x)) : x => WebsocketDispatcher.Schedule(() => SendPongAsync(x)); - OnPong = (onPong != null) ? x => WebsocketDispatcher.Schedule(() => onPong(this, x)) : x => { }; - OnError = (onError != null) ? x => WebsocketDispatcher.Schedule(() => onError(this, x)) : x => { }; - } - - #endregion - - #region Functionality - - public Task Send(string message) => SendAsync(message); - - public Task Send(byte[] message) => SendAsync(message); - - public Task SendPing(byte[] message) => SendPingAsync(message); - - public Task SendPong(byte[] message) => SendPongAsync(message); - - public Task SendAsync(string message) => Send(message, GetHandler().FrameText); - - public Task SendAsync(byte[] message) => Send(message, GetHandler().FrameBinary); - - public Task SendPingAsync(byte[] message) => Send(message, GetHandler().FramePing); - - public Task SendPongAsync(byte[] message) => Send(message, GetHandler().FramePong); - - private Task Send(T message, Func createFrame) - { - if (Handler == null) - throw new InvalidOperationException("Cannot send before handshake"); - - if (!IsAvailable) - { - const string errorMessage = "Data sent while closing or after close. Ignoring."; - FleckLog.Warn(errorMessage); - - var taskForException = new TaskCompletionSource(); - taskForException.SetException(new ConnectionNotAvailableException(errorMessage)); - return taskForException.Task; - } - - var bytes = createFrame(message); - - return SendBytes(bytes); - } - - public void Start() - { - var mappedRequest = Request.Map(); - - Handler = HandlerFactory.BuildHandler(mappedRequest, OnMessage, OnClose, OnBinary, OnPing, OnPong); - - var subProtocol = SubProtocolNegotiator.Negotiate(SupportedProtocols, mappedRequest.SubProtocols); - ConnectionInfo = WebSocketConnectionInfo.Create(mappedRequest, Socket.RemoteIpAddress, Socket.RemotePort, subProtocol); - - var handshake = Handler.CreateHandshake(subProtocol); - SendBytes(handshake, OnOpen); - - _readingTask = StartReading(); - } - - public void Close() - { - Close(WebSocketStatusCodes.NormalClosure); - } - - public void Close(int code) - { - if (!IsAvailable) - return; - - _closing = true; - - if (Handler == null) - { - CloseSocket(); - return; - } - - var bytes = Handler.FrameClose(code); - - if (bytes.Length == 0) - CloseSocket(); - else - SendBytes(bytes, CloseSocket); - } - - private Task StartReading() - { - return Task.Run(async () => - { - var buffer = new byte[ReadSize]; - - var handler = GetHandler(); - - while (IsAvailable) - { - var read = await Socket.Receive(buffer, _ => { }, HandleReadError); - - if (!IsAvailable) return; - - if (read <= 0) - { - FleckLog.Debug("0 bytes read. Closing."); - CloseSocket(); - return; - } - - FleckLog.Debug(read + " bytes read"); - - var readBytes = buffer.Take(read); - - handler.Receive(readBytes); - } - }); - } - - private void HandleReadError(Exception e) - { - if (e is AggregateException agg) - { - if (agg.InnerException != null) - { - HandleReadError(agg.InnerException); - } - - return; - } - - if (e is ObjectDisposedException) - { - FleckLog.Debug("Swallowing ObjectDisposedException", e); - return; - } - - OnError(e); - - if (e is WebSocketException exception) - { - FleckLog.Debug("Error while reading", exception); - Close(exception.StatusCode); - } - else if (e is SubProtocolNegotiationFailureException) - { - FleckLog.Debug(e.Message); - Close(WebSocketStatusCodes.ProtocolError); - } - else if (e is IOException) - { - FleckLog.Debug("Error while reading", e); - Close(WebSocketStatusCodes.AbnormalClosure); - } - else - { - FleckLog.Error("Application Error", e); - Close(WebSocketStatusCodes.InternalServerError); - } - } - - private Task SendBytes(byte[] bytes, Action? callback = null) - { - return Socket.Send(bytes, () => - { - FleckLog.Debug("Sent " + bytes.Length + " bytes"); - callback?.Invoke(); - }, - e => - { - if (e is IOException) - FleckLog.Debug("Failed to send. Disconnecting.", e); - else - FleckLog.Info("Failed to send. Disconnecting.", e); - CloseSocket(); - }); - } - - private void CloseSocket() - { - if (_readingTask != null) - { - _readingTask.Dispose(); - _readingTask = null; - } - - _closing = true; - OnClose(); - _closed = true; - Socket.Close(); - Socket.Dispose(); - _closing = false; - } - - private IHandler GetHandler() => Handler ?? throw new InvalidOperationException("Handler expected but not set"); - - #endregion - -} diff --git a/Modules/Websockets/Legacy/WebsocketDispatcher.cs b/Modules/Websockets/Legacy/WebsocketDispatcher.cs deleted file mode 100644 index d9325deeb..000000000 --- a/Modules/Websockets/Legacy/WebsocketDispatcher.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Fleck; - -namespace GenHTTP.Modules.Websockets.Legacy; - -/// -/// Flecks uses synchronous callbacks for integration of user -/// logic, but provides asynchronous methods for interacting -/// with the websocket connection, which is counter-intuitive -/// and leads to errors. GenHTTP´s integration forces asynchronous -/// callbacks to be supplied by the user which requires us to dispatch -/// them on the synchronous callbacks invoked by Fleck. -/// -public static class WebsocketDispatcher -{ - - /// - /// Schedules the given piece of work on a background - /// thread and logs any error using the regular Fleck - /// logging mechanism. - /// - /// The actual piece of work to be executed - public static void Schedule(Func work) - { - _ = Task.Run(async () => - { - try - { - await work().ConfigureAwait(false); - } - catch (Exception e) - { - FleckLog.Error("Failed to run asynchronous event handler.", e); - } - }); - } - -} diff --git a/Modules/Websockets/Legacy/WebsocketHandler.cs b/Modules/Websockets/Legacy/WebsocketHandler.cs deleted file mode 100644 index 07033819b..000000000 --- a/Modules/Websockets/Legacy/WebsocketHandler.cs +++ /dev/null @@ -1,83 +0,0 @@ -using GenHTTP.Api.Content; -using GenHTTP.Api.Protocol; -using GenHTTP.Modules.Websockets.Utils; -using IHandler = GenHTTP.Api.Content.IHandler; - -namespace GenHTTP.Modules.Websockets.Legacy; - -public sealed class WebsocketHandler : IHandler -{ - - #region Get-/Setters - - public Func? OnOpen { get; } - - public Func? OnClose { get; } - - public Func? OnMessage { get; } - - public Func? OnBinary { get; } - - public Func? OnPing { get; } - - public Func? OnPong { get; } - - public Func? OnError { get; } - - public List SupportedProtocols { get; } - - #endregion - - #region Initialization - - public WebsocketHandler(List supportedProtocols, - Func? onOpen, - Func? onClose, - Func? onMessage, - Func? onBinary, - Func? onPing, - Func? onPong, - Func? onError) - { - SupportedProtocols = supportedProtocols; - - OnOpen = onOpen; - OnClose = onClose; - OnMessage = onMessage; - OnBinary = onBinary; - OnPing = onPing; - OnPong = onPong; - OnError = onError; - } - - #endregion - - #region Functionality - - public ValueTask PrepareAsync() => new(); - - public ValueTask HandleAsync(IRequest request) - { - if (!request.Headers.ContainsKey("Upgrade") || request.Headers["Upgrade"] != "websocket") - { - throw new ProviderException(ResponseStatus.BadRequest, "Websocket upgrade request expected"); - } - - var upgrade = request.Upgrade(); - - var socket = new WrappedSocket(upgrade); - - // the server will re-use the request for a new client, so - // we need to freeze the values to allow access from the socket context - var clone = ClonedRequest.From(request); - - var connection = new WebsocketConnection(socket, clone, SupportedProtocols, OnOpen, OnClose, OnMessage, OnBinary, OnPing, OnPong, OnError); - - connection.Start(); - - return new(upgrade.Response); - } - - #endregion - -} diff --git a/Modules/Websockets/Legacy/WebsocketHandlerBuilder.cs b/Modules/Websockets/Legacy/WebsocketHandlerBuilder.cs deleted file mode 100644 index ab4450b1c..000000000 --- a/Modules/Websockets/Legacy/WebsocketHandlerBuilder.cs +++ /dev/null @@ -1,114 +0,0 @@ -using GenHTTP.Api.Content; - -namespace GenHTTP.Modules.Websockets.Legacy; - -public class WebsocketHandlerBuilder : IHandlerBuilder -{ - private readonly List _concerns = []; - - private readonly List _supportedProtocols = []; - - private Func? _onOpen; - private Func? _onClose; - private Func? _onMessage; - private Func? _onBinary; - private Func? _onPing; - private Func? _onPong; - private Func? _onError; - - #region Functionality - - public WebsocketHandlerBuilder Add(IConcernBuilder concern) - { - _concerns.Add(concern); - return this; - } - - /// - /// Specifies a protocol supported by this websocket server. - /// - /// The name of the supported protocol - public WebsocketHandlerBuilder Protocol(string supportedProtocol) - { - _supportedProtocols.Add(supportedProtocol); - return this; - } - - /// - /// Will be executed if a new websocket client connected. - /// - /// The method to be executed - public WebsocketHandlerBuilder OnOpen(Func handler) - { - _onOpen = handler; - return this; - } - - /// - /// Will be executed if a websocket client disconnects. - /// - /// The method to be executed - public WebsocketHandlerBuilder OnClose(Func handler) - { - _onClose = handler; - return this; - } - - /// - /// Will be executed if a string message has been received from the client. - /// - /// The method to be executed - public WebsocketHandlerBuilder OnMessage(Func handler) - { - _onMessage = handler; - return this; - } - - /// - /// Will be executed if a binary message has been received from the client. - /// - /// The method to be executed - public WebsocketHandlerBuilder OnBinary(Func handler) - { - _onBinary = handler; - return this; - } - - /// - /// Will be executed if the client sends a ping request. - /// - /// The method to be executed - public WebsocketHandlerBuilder OnPing(Func handler) - { - _onPing = handler; - return this; - } - - /// - /// Will be executed if the client sends a pong request. - /// - /// The method to be executed - public WebsocketHandlerBuilder OnPong(Func handler) - { - _onPong = handler; - return this; - } - - /// - /// Will be executed if there is some client connection error. - /// - /// The method to be executed - public WebsocketHandlerBuilder OnError(Func handler) - { - _onError = handler; - return this; - } - - public IHandler Build() - { - return Concerns.Chain(_concerns, new WebsocketHandler(_supportedProtocols, _onOpen, _onClose, _onMessage, _onBinary, _onPing, _onPong, _onError)); - } - - #endregion - -} diff --git a/Modules/Websockets/Legacy/WrappedSocket.cs b/Modules/Websockets/Legacy/WrappedSocket.cs deleted file mode 100644 index a5f797873..000000000 --- a/Modules/Websockets/Legacy/WrappedSocket.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System.Net; -using System.Net.Sockets; -using System.Security.Authentication; -using System.Security.Cryptography.X509Certificates; -using Fleck; -using GenHTTP.Api.Protocol; - -namespace GenHTTP.Modules.Websockets.Legacy; - -public sealed class WrappedSocket : ISocket -{ - - #region Get-/Setters - - public Socket Socket { get; } - - public Stream Stream { get; } - - public CancellationTokenSource TokenSource { get; } - - public bool Connected => Socket.Connected; - - public bool NoDelay - { - get => Socket.NoDelay; - set => Socket.NoDelay = value; - } - - public string RemoteIpAddress => (Socket.RemoteEndPoint as IPEndPoint)?.Address.ToString() ?? string.Empty; - - public int RemotePort => (Socket.RemoteEndPoint as IPEndPoint)?.Port ?? -1; - - #endregion - - #region Initialization - - public WrappedSocket(UpgradeInfo upgradeInfo) - { - Socket = upgradeInfo.Socket; - Stream = upgradeInfo.Stream; - - TokenSource = new(); - } - - #endregion - - #region Functionality - - public async Task Send(byte[] buffer, Action callback, Action error) - { - try - { - await Stream.WriteAsync(buffer.AsMemory(), TokenSource.Token); - await Stream.FlushAsync(); - - callback(); - } - catch (Exception ex) - { - error(ex); - } - } - - public async Task Receive(byte[] buffer, Action callback, Action error, int offset = 0) - { - try - { - var result = await Stream.ReadAsync(buffer.AsMemory(offset), TokenSource.Token); - - callback(result); - - return result; - } - catch (Exception ex) - { - error(ex); - return -1; - } - } - - public void Close() => Dispose(); - - public void Dispose() - { - TokenSource.Cancel(); - Stream.Dispose(); - Socket.Dispose(); - } - - #endregion - - #region Unnecessary stuff - - private const string NotRequiredByIntegration = "Not required by integration"; - - public EndPoint LocalEndPoint => throw new NotImplementedException(NotRequiredByIntegration); - - public Task Accept(Action callback, Action error) - => throw new NotImplementedException(NotRequiredByIntegration); - - public void Bind(EndPoint ipLocal) - => throw new NotImplementedException(NotRequiredByIntegration); - - public void Listen(int backlog) - => throw new NotImplementedException(NotRequiredByIntegration); - - public Task Authenticate(X509Certificate2 certificate, SslProtocols enabledSslProtocols, Action callback, Action error) - => throw new NotImplementedException(NotRequiredByIntegration); - - #endregion - -} diff --git a/Modules/Websockets/Utils/ClonedRequest.cs b/Modules/Websockets/Utils/ClonedRequest.cs deleted file mode 100644 index 27ccb79f9..000000000 --- a/Modules/Websockets/Utils/ClonedRequest.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -using GenHTTP.Api.Infrastructure; -using GenHTTP.Api.Protocol; -using GenHTTP.Api.Routing; - -using Shared = GenHTTP.Engine.Shared.Types; - -namespace GenHTTP.Modules.Websockets.Utils; - -public class ClonedRequest : IRequest -{ - - #region Get-/Setters - - public IServer Server { get; } - - public IEndPoint EndPoint { get; } - - public IClientConnection Client { get; } - - public IClientConnection LocalClient { get; } - - public HttpProtocol ProtocolType { get; } - - public FlexibleRequestMethod Method { get; } - - public RoutingTarget Target { get; } - - public string? UserAgent => this["UserAgent"]; - - public string? Referer => this["Referer"]; - - public string? Host => this["Host"]; - - public string? this[string additionalHeader] => Headers.GetValueOrDefault(additionalHeader); - - public IRequestQuery Query { get; } - - public ICookieCollection Cookies { get; } - - public IForwardingCollection Forwardings { get; } - - public IHeaderCollection Headers { get; } - - public Stream? Content => null; - - public FlexibleContentType? ContentType { get; } - - public IRequestProperties Properties { get; } - - #endregion - - #region Initialization - - public static ClonedRequest From(IRequest request) - { - var query = new RequestQuery(request.Query); - var cookies = new CookieCollection(request.Cookies); - var headers = new HeaderCollection(request.Headers); - var forwardings = new ForwardingCollection(request.Forwardings); - var properties = new RequestProperties(request.Properties); - - return new ClonedRequest(request.Server, request.EndPoint, request.Client, request.LocalClient, - request.ProtocolType, request.Method, request.Target, query, cookies, forwardings, headers, - request.ContentType, properties); - } - - private ClonedRequest(IServer server, IEndPoint endpoint, IClientConnection client, IClientConnection localClient, - HttpProtocol protocol, FlexibleRequestMethod method, RoutingTarget target, IRequestQuery query, - ICookieCollection cookies, IForwardingCollection forwardings, IHeaderCollection headers, - FlexibleContentType? contentType, IRequestProperties properties) - { - Server = server; - EndPoint = endpoint; - Client = client; - LocalClient = localClient; - ProtocolType = protocol; - Method = method; - Target = target; - Cookies = cookies; - Forwardings = forwardings; - Query = query; - Properties = properties; - Headers = headers; - ContentType = contentType; - } - - #endregion - - #region Functionality - - public IResponseBuilder Respond() => throw new NotSupportedException(); - - public UpgradeInfo Upgrade() => throw new NotSupportedException(); - - public void Dispose() - { - // nop - } - - #endregion - -} - -internal class RequestQuery : Dictionary, IRequestQuery -{ - - internal RequestQuery(IRequestQuery query) - { - foreach (var pair in query) - { - Add(pair.Key, pair.Value); - } - } - -} - -internal class CookieCollection : Dictionary, ICookieCollection -{ - - internal CookieCollection(ICookieCollection cookies) - { - foreach (var pair in cookies) - { - Add(pair.Key, pair.Value); - } - } - -} - - -internal class HeaderCollection : Shared.MultiEntryDictionary, IHeaderCollection -{ - - internal HeaderCollection(IHeaderCollection header) : base(header.Count, StringComparer.InvariantCultureIgnoreCase) - { - foreach (var pair in header) - { - base.Add(pair.Key, pair.Value); - } - } - -} - -internal class ForwardingCollection : List, IForwardingCollection -{ - - internal ForwardingCollection(IForwardingCollection forwardings) - { - foreach (var forwarding in forwardings) - { - Add(forwarding); - } - } - -} - -internal class RequestProperties : IRequestProperties -{ - private readonly Dictionary _data; - - internal RequestProperties(IRequestProperties source) - { - _data = []; - - if (source is Shared.RequestProperties cloneable) - { - cloneable.CloneTo(_data); - } - } - - public object this[string key] - { - get => _data[key] ?? throw new KeyNotFoundException(key); - set => _data[key] = value; - } - - public bool TryGet(string key, [MaybeNullWhen(false)] out T entry) - { - if (_data.TryGetValue(key, out var value)) - { - if (value is T tValue) - { - entry = tValue; - return true; - } - } - - entry = default; - return false; - } - - public void Clear(string key) - { - _data.Remove(key); - } - -} diff --git a/Modules/Websockets/Websocket.cs b/Modules/Websockets/Websocket.cs index d35fcc3c9..c48a94448 100644 --- a/Modules/Websockets/Websocket.cs +++ b/Modules/Websockets/Websocket.cs @@ -1,6 +1,5 @@ using GenHTTP.Modules.Websockets.Functional; using GenHTTP.Modules.Websockets.Imperative; -using GenHTTP.Modules.Websockets.Legacy; using GenHTTP.Modules.Websockets.Reactive; namespace GenHTTP.Modules.Websockets; @@ -13,13 +12,6 @@ namespace GenHTTP.Modules.Websockets; public static class Websocket { - /// - /// Creates a new builder to configure a websocket handler - /// that will process incoming websocket requests. - /// - [Obsolete("The web socket implementation based on Fleck will be removed with GenHTTP 11.")] - public static WebsocketHandlerBuilder Create() => new(); - /// /// Creates a web socket that allows you to read /// messages one-by-one from the socket connection and handle diff --git a/Packages/GenHTTP.Core.Kestrel.nuspec b/Packages/GenHTTP.Core.Kestrel.nuspec index 765501568..06ea11b76 100644 --- a/Packages/GenHTTP.Core.Kestrel.nuspec +++ b/Packages/GenHTTP.Core.Kestrel.nuspec @@ -3,7 +3,7 @@ GenHTTP.Core.Kestrel - 10.5.0 + 11.0.0 Basic dependencies for projects using the GenHTTP framework, combined with the Kestrel HTTP engine to serve requests @@ -22,13 +22,13 @@ - + - + - - - + + + diff --git a/Packages/GenHTTP.Core.nuspec b/Packages/GenHTTP.Core.nuspec index a017e20ce..51dce297d 100644 --- a/Packages/GenHTTP.Core.nuspec +++ b/Packages/GenHTTP.Core.nuspec @@ -3,7 +3,7 @@ GenHTTP.Core - 10.5.0 + 11.0.0 Basic dependencies for projects using the GenHTTP framework, including the engine itself @@ -22,13 +22,13 @@ - + - + - - - + + + diff --git a/README.md b/README.md index 095361af8..41ccf002f 100644 --- a/README.md +++ b/README.md @@ -139,4 +139,4 @@ in C#. In 2024 the focus has shifted towards API development, dropping support f ## 🙏 Thanks - Powered by [.NET](https://github.com/dotnet/core) -- Modules implemented with [NSwag](https://github.com/RicoSuter/NSwag) | [Cottle](https://r3c.github.io/cottle/) | [SharpCompress](https://github.com/adamhathcock/sharpcompress) | [Fleck](https://github.com/statianzo/Fleck) +- Modules implemented with [NSwag](https://github.com/RicoSuter/NSwag) | [Cottle](https://r3c.github.io/cottle/) | [SharpCompress](https://github.com/adamhathcock/sharpcompress) diff --git a/Testing/Acceptance/Engine/KeepAliveTests.cs b/Testing/Acceptance/Engine/KeepAliveTests.cs deleted file mode 100644 index e89787942..000000000 --- a/Testing/Acceptance/Engine/KeepAliveTests.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Net; -using System.Net.Sockets; - -using GenHTTP.Api.Content; -using GenHTTP.Api.Protocol; - -using GenHTTP.Modules.Functional; - -namespace GenHTTP.Testing.Acceptance.Engine; - -[TestClass] -public class KeepAliveTests -{ - - /// - /// This is a white box test that verifies that two subsequent requests - /// to the server from the same client reuse the same connection, - /// thus verifying that keep-alive connections are supported. - /// - [TestMethod] - public async Task TestKeepAlive() - { - Socket? connection = null; - - var tester = Inline.Create() - .Get((IRequest r) => - { - var socket = r.Upgrade().Socket; - - if (connection == null) - { - connection = socket; - } - else - { - if (connection != socket) - { - throw new ProviderException(ResponseStatus.BadRequest, "Client connection did change, so this is not a keep-alive request"); - } - } - }); - - await using var runner = await TestHost.RunAsync(tester, engine: TestEngine.Internal); - - using var r1 = await runner.GetResponseAsync(); - await r1.AssertStatusAsync(HttpStatusCode.NoContent); - - using var r2 = await runner.GetResponseAsync(); - await r2.AssertStatusAsync(HttpStatusCode.NoContent); - } - -} diff --git a/Testing/Acceptance/GenHTTP.Testing.Acceptance.csproj b/Testing/Acceptance/GenHTTP.Testing.Acceptance.csproj index a5bbdcce5..2a4cf7c92 100644 --- a/Testing/Acceptance/GenHTTP.Testing.Acceptance.csproj +++ b/Testing/Acceptance/GenHTTP.Testing.Acceptance.csproj @@ -4,9 +4,6 @@ false - - CS1591,CS1587,CS1572,CS1573,CS0612,CS0618 - diff --git a/Testing/Acceptance/Modules/Websockets/Integration/Functional/IntegrationTests.cs b/Testing/Acceptance/Modules/Websockets/Integration/Functional/IntegrationTests.cs index e95fa638f..2c8ab04da 100644 --- a/Testing/Acceptance/Modules/Websockets/Integration/Functional/IntegrationTests.cs +++ b/Testing/Acceptance/Modules/Websockets/Integration/Functional/IntegrationTests.cs @@ -19,8 +19,8 @@ public async Task TestServerFunctional() .OnMessage((c, m) => c.WriteAsync(m.Data)) .OnContinue((c, m) => c.WriteAsync(m.Data)) .OnPing((c, m) => c.PongAsync(m.Data)) - .OnClose((c, m) => c.CloseAsync()) - .OnError((c, e) => ValueTask.FromResult(false)); + .OnClose((c, _) => c.CloseAsync()) + .OnError((_, _) => ValueTask.FromResult(false)); Chain.Works(websocket); @@ -70,8 +70,8 @@ public async Task TestServerFunctionalSegmented() .OnMessage((c, m) => c.WriteAsync(m.Data)) .OnContinue((c, m) => c.WriteAsync(m.Data)) .OnPing((c, m) => c.PongAsync(m.Data)) - .OnClose((c, m) => c.CloseAsync()) - .OnError((c, e) => ValueTask.FromResult(false)); + .OnClose((c, _) => c.CloseAsync()) + .OnError((_, _) => ValueTask.FromResult(false)); Chain.Works(websocket); @@ -86,12 +86,12 @@ public async Task TestServerFunctionalSegmented() public async Task TestServerFunctionalFragmented() { var websocket = GenHTTP.Modules.Websockets.Websocket.Functional() - .OnConnected(c => ValueTask.CompletedTask) + .OnConnected(_ => ValueTask.CompletedTask) .OnMessage((c, m) => c.WriteAsync(m.Data)) .OnContinue((c, m) => c.WriteAsync(m.Data)) .OnPing((c, m) => c.PongAsync(m.Data)) - .OnClose((c, m) => c.CloseAsync()) - .OnError((c, e) => ValueTask.FromResult(false)); + .OnClose((c, _) => c.CloseAsync()) + .OnError((_, _) => ValueTask.FromResult(false)); Chain.Works(websocket); @@ -107,12 +107,12 @@ public async Task TestServerFunctionalFragmented() public async Task TestServerFunctionalFragmentedSegmented() { var websocket = GenHTTP.Modules.Websockets.Websocket.Functional() - .OnConnected(c => ValueTask.CompletedTask) + .OnConnected(_ => ValueTask.CompletedTask) .OnMessage((c, m) => c.WriteAsync(m.Data)) .OnContinue((c, m) => c.WriteAsync(m.Data)) .OnPing((c, m) => c.PongAsync(m.Data)) - .OnClose((c, m) => c.CloseAsync()) - .OnError((c, e) => ValueTask.FromResult(false)); + .OnClose((c, _) => c.CloseAsync()) + .OnError((_, _) => ValueTask.FromResult(false)); Chain.Works(websocket); @@ -129,12 +129,12 @@ public async Task TestServerFunctionalFragmentedSegmented() public async Task TestServerFunctionalFragmentedSegmentedNoAllocations() { var websocket = GenHTTP.Modules.Websockets.Websocket.Functional() - .OnConnected(c => ValueTask.CompletedTask) + .OnConnected(_ => ValueTask.CompletedTask) .OnMessage((c, m) => c.WriteAsync(m.Data)) .OnContinue((c, m) => c.WriteAsync(m.Data)) .OnPing((c, m) => c.PongAsync(m.Data)) - .OnClose((c, m) => c.CloseAsync()) - .OnError((c, e) => ValueTask.FromResult(false)); + .OnClose((c, _) => c.CloseAsync()) + .OnError((_, _) => ValueTask.FromResult(false)); Chain.Works(websocket); diff --git a/Testing/Acceptance/Modules/Websockets/Legacy/ErrorHandlingTests.cs b/Testing/Acceptance/Modules/Websockets/Legacy/ErrorHandlingTests.cs deleted file mode 100644 index 01d3df9df..000000000 --- a/Testing/Acceptance/Modules/Websockets/Legacy/ErrorHandlingTests.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Net; -using Websocket.Client; -using WS = GenHTTP.Modules.Websockets.Websocket; - -namespace GenHTTP.Testing.Acceptance.Modules.Websockets.Legacy; - -[TestClass] -public sealed class ErrorHandlingTests -{ - - [TestMethod] - public async Task TestInvalidRequest() - { - await using var host = await TestHost.RunAsync(WS.Create()); - - using var response = await host.GetResponseAsync(); - - Assert.AreEqual(HttpStatusCode.BadRequest, response.StatusCode); - } - - [TestMethod] - public async Task TestErrorHandling() - { - var server = WS.Create() - .OnOpen(_ => throw new InvalidOperationException("Ooops")); - - await using var host = await TestHost.RunAsync(server); - - using var client = new WebsocketClient(new Uri("ws://localhost:" + host.Port)); - - await client.Start(); - - await Task.Delay(1000); - } - -} diff --git a/Testing/Acceptance/Modules/Websockets/Legacy/InitializerTest.cs b/Testing/Acceptance/Modules/Websockets/Legacy/InitializerTest.cs deleted file mode 100644 index 66892c3ac..000000000 --- a/Testing/Acceptance/Modules/Websockets/Legacy/InitializerTest.cs +++ /dev/null @@ -1,23 +0,0 @@ -using WS = GenHTTP.Modules.Websockets; - -namespace GenHTTP.Testing.Acceptance.Modules.Websockets.Legacy; - -[TestClass] -public class InitializerTest -{ - - [TestMethod] - public void InitializeAll() - { - WS.Websocket.Create() - .OnOpen(s => Task.CompletedTask) - .OnClose(s => Task.CompletedTask) - .OnPing(async (s, b) => { await s.SendPongAsync(b); }) - .OnPong((s, b) => Task.CompletedTask) - .OnMessage((s, x) => Task.CompletedTask) - .OnBinary((s, x) => Task.CompletedTask) - .OnError((s, x) => Task.CompletedTask) - .Protocol("chat"); - } - -} diff --git a/Testing/Acceptance/Modules/Websockets/Legacy/IntegrationTest.cs b/Testing/Acceptance/Modules/Websockets/Legacy/IntegrationTest.cs deleted file mode 100644 index 2a27b2137..000000000 --- a/Testing/Acceptance/Modules/Websockets/Legacy/IntegrationTest.cs +++ /dev/null @@ -1,92 +0,0 @@ -using GenHTTP.Testing.Acceptance.Utilities; -using Websocket.Client; -using WS = GenHTTP.Modules.Websockets; - -namespace GenHTTP.Testing.Acceptance.Modules.Websockets.Legacy; - -[TestClass] -public sealed class IntegrationTest -{ - - [TestMethod] - public async Task TestServer() - { - var waitEvent = new ManualResetEvent(false); - - var length = 0; - - var server = WS.Websocket.Create() - .OnMessage(async (socket, msg) => - { - length += msg.Length; - - await socket.SendAsync(msg); - - socket.Close(); - }); - - Chain.Works(server); - - await using var host = await TestHost.RunAsync(server); - - using var client = new WebsocketClient(new Uri("ws://localhost:" + host.Port)); - - client.MessageReceived.Subscribe(msg => - { - length += msg.Text?.Length ?? 0; - waitEvent.Set(); - }); - - await client.Start(); - - _ = Task.Run(() => client.Send("1234567890")); - - Assert.IsTrue(waitEvent.WaitOne(TimeSpan.FromSeconds(5))); - - Assert.AreEqual(20, length); - } - - [TestMethod] - public async Task TestDataTypes() - { - var waitEvent = new ManualResetEvent(false); - - var server = WS.Websocket.Create() - .OnOpen(async (socket) => - { - await socket.SendPingAsync([42]); - await socket.SendPongAsync([42]); - - await socket.SendAsync([42]); - - Assert.IsTrue(socket.IsAvailable); - - var request = socket.Request; - - Assert.IsNotEmpty(request.Headers); - - Assert.IsEmpty(request.Cookies); - - Assert.IsEmpty(request.Forwardings); - - Assert.Contains("localhost", request.Host ?? string.Empty); - - Assert.Throws(() => request.Upgrade()); - - Assert.Throws(() => request.Respond()); - - socket.Close(42); - - waitEvent.Set(); - }); - - await using var host = await TestHost.RunAsync(server); - - using var client = new WebsocketClient(new Uri("ws://localhost:" + host.Port)); - - await client.Start(); - - Assert.IsTrue(waitEvent.WaitOne(TimeSpan.FromSeconds(5))); - } - -}