From e5b6f5137ce32d173301c288ee3fe6be29e4177a Mon Sep 17 00:00:00 2001 From: cypherpotato Date: Sun, 1 Sep 2024 16:58:45 -0300 Subject: [PATCH] fix sslproxy chunked reader --- core.sln | 52 ------------------- .../Sisk.IniConfiguration.csproj | 6 +-- extensions/Sisk.SslProxy/SerializerUtils.cs | 4 +- extensions/Sisk.SslProxy/SslProxy.cs | 3 +- src/Entity/StringValue.cs | 23 ++++++++ src/Entity/StringValueCollection.cs | 10 ++++ .../Handlers/HttpServerHandlerRepository.cs | 52 +++++++++++++------ .../Hosting/PortableConfigurationBuilder.cs | 34 ++++++------ src/Http/Streams/HttpWebSocket.cs | 48 ++++++++++++++++- src/Routing/Route.cs | 41 ++++++--------- src/Sisk.Core.csproj | 6 +-- 11 files changed, 158 insertions(+), 121 deletions(-) delete mode 100644 core.sln diff --git a/core.sln b/core.sln deleted file mode 100644 index b9eef64..0000000 --- a/core.sln +++ /dev/null @@ -1,52 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sisk.Core", "src\Sisk.Core.csproj", "{668093F7-5178-4BE5-9B3C-26BCAE91604E}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "extensions", "extensions", "{91E8D326-5CEC-4981-8364-2CCAE6C9F08E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sisk.BasicAuth", "extensions\Sisk.BasicAuth\Sisk.BasicAuth.csproj", "{43E54096-257E-4B26-B8EA-E02E6BDDE166}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sisk.ServiceProvider", "extensions\Sisk.ServiceProvider\Sisk.ServiceProvider.csproj", "{5CD2E0A6-80FA-46C1-9C6C-51C0CE3FFE35}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7F25A948-01E1-4958-8012-CD62CEDA56B4}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeAOT_Test", "tests\NativeAOT_Test\NativeAOT_Test.csproj", "{9E711B3C-4CCA-4F81-BDED-9BD4D9CC35B9}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {668093F7-5178-4BE5-9B3C-26BCAE91604E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {668093F7-5178-4BE5-9B3C-26BCAE91604E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {668093F7-5178-4BE5-9B3C-26BCAE91604E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {668093F7-5178-4BE5-9B3C-26BCAE91604E}.Release|Any CPU.Build.0 = Release|Any CPU - {43E54096-257E-4B26-B8EA-E02E6BDDE166}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {43E54096-257E-4B26-B8EA-E02E6BDDE166}.Debug|Any CPU.Build.0 = Debug|Any CPU - {43E54096-257E-4B26-B8EA-E02E6BDDE166}.Release|Any CPU.ActiveCfg = Release|Any CPU - {43E54096-257E-4B26-B8EA-E02E6BDDE166}.Release|Any CPU.Build.0 = Release|Any CPU - {5CD2E0A6-80FA-46C1-9C6C-51C0CE3FFE35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5CD2E0A6-80FA-46C1-9C6C-51C0CE3FFE35}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5CD2E0A6-80FA-46C1-9C6C-51C0CE3FFE35}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5CD2E0A6-80FA-46C1-9C6C-51C0CE3FFE35}.Release|Any CPU.Build.0 = Release|Any CPU - {9E711B3C-4CCA-4F81-BDED-9BD4D9CC35B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9E711B3C-4CCA-4F81-BDED-9BD4D9CC35B9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9E711B3C-4CCA-4F81-BDED-9BD4D9CC35B9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9E711B3C-4CCA-4F81-BDED-9BD4D9CC35B9}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {43E54096-257E-4B26-B8EA-E02E6BDDE166} = {91E8D326-5CEC-4981-8364-2CCAE6C9F08E} - {5CD2E0A6-80FA-46C1-9C6C-51C0CE3FFE35} = {91E8D326-5CEC-4981-8364-2CCAE6C9F08E} - {9E711B3C-4CCA-4F81-BDED-9BD4D9CC35B9} = {7F25A948-01E1-4958-8012-CD62CEDA56B4} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {274605EE-9A2C-4340-AD03-D620BBEE362B} - EndGlobalSection -EndGlobal diff --git a/extensions/Sisk.IniConfiguration/Sisk.IniConfiguration.csproj b/extensions/Sisk.IniConfiguration/Sisk.IniConfiguration.csproj index 1e30610..617b4e9 100644 --- a/extensions/Sisk.IniConfiguration/Sisk.IniConfiguration.csproj +++ b/extensions/Sisk.IniConfiguration/Sisk.IniConfiguration.csproj @@ -21,9 +21,9 @@ http-server,http,web framework git - 1.0.0.0 - 1.0.0.0 - 1.0.0.0 + 1.0.1.0 + 1.0.1.0 + 1.0.1.0 en LICENSE.txt diff --git a/extensions/Sisk.SslProxy/SerializerUtils.cs b/extensions/Sisk.SslProxy/SerializerUtils.cs index 84c8087..d56a2ad 100644 --- a/extensions/Sisk.SslProxy/SerializerUtils.cs +++ b/extensions/Sisk.SslProxy/SerializerUtils.cs @@ -46,10 +46,10 @@ public static void CopyBlocking(Stream input, Stream output, EventWaitHandle wai callback = ar => { int bytesRead = input.EndRead(ar); + output.Write(buffer, 0, bytesRead); if (bytesRead > 0) { - output.Write(buffer, 0, bytesRead); input.BeginRead(buffer, 0, buffer.Length, callback, null); } else @@ -69,11 +69,11 @@ public static void CopyUntilBlocking(Stream input, Stream output, byte[] eof, Ev callback = ar => { int bytesRead = input.EndRead(ar); + output.Write(buffer, 0, bytesRead); ReadOnlySpan writtenSpan = buffer[0..bytesRead]; if (bytesRead > 0 && !writtenSpan.EndsWith(eof)) { - output.Write(buffer, 0, bytesRead); input.BeginRead(buffer, 0, buffer.Length, callback, null); } else diff --git a/extensions/Sisk.SslProxy/SslProxy.cs b/extensions/Sisk.SslProxy/SslProxy.cs index 9ed90f8..de63f8e 100644 --- a/extensions/Sisk.SslProxy/SslProxy.cs +++ b/extensions/Sisk.SslProxy/SslProxy.cs @@ -165,7 +165,8 @@ void ReceiveClientAsync(IAsyncResult ar) // TODO: check if client wants to keep alive if (isConnectionKeepAlive) { - resHeaders.Add(("Connection", "keep-alive")); + // not necessary in HTTP/1.1 + // resHeaders.Add(("Connection", "keep-alive")); } else { diff --git a/src/Entity/StringValue.cs b/src/Entity/StringValue.cs index 07b8b68..b20a00b 100644 --- a/src/Entity/StringValue.cs +++ b/src/Entity/StringValue.cs @@ -27,6 +27,29 @@ internal StringValue(string name, string type, string? data) this.argType = type; } + /// + /// Creates an new empty value of the with no predefined value. + /// + /// The name. + public StringValue(string name) + { + this._ref = null; + this.argName = name; + this.argType = "StringValue"; + } + + /// + /// Creates an new value of the . + /// + /// The name. + /// The value. + public StringValue(string name, string? value) + { + this._ref = value; + this.argName = name; + this.argType = "StringValue"; + } + /// /// Gets the name of the property that hosts this . /// diff --git a/src/Entity/StringValueCollection.cs b/src/Entity/StringValueCollection.cs index 2a6c9c0..87903a5 100644 --- a/src/Entity/StringValueCollection.cs +++ b/src/Entity/StringValueCollection.cs @@ -50,6 +50,16 @@ internal static StringValueCollection FromNameValueCollection(string paramName, return vcol; } + /// + /// Creates an new instance with values from another + /// instance. + /// + public StringValueCollection(IDictionary values) + { + this.items = new Dictionary(values, StringComparer.OrdinalIgnoreCase); + this.paramName = "StringValue"; + } + internal StringValueCollection(string paramName) { this.items = new Dictionary(StringComparer.OrdinalIgnoreCase); diff --git a/src/Http/Handlers/HttpServerHandlerRepository.cs b/src/Http/Handlers/HttpServerHandlerRepository.cs index 26b9e1f..a9ed688 100644 --- a/src/Http/Handlers/HttpServerHandlerRepository.cs +++ b/src/Http/Handlers/HttpServerHandlerRepository.cs @@ -7,13 +7,26 @@ // File name: HttpServerHandlerRepository.cs // Repository: https://github.com/sisk-http/core -using Sisk.Core.Entity; -using Sisk.Core.Routing; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Sisk.Core.Entity; +using Sisk.Core.Routing; namespace Sisk.Core.Http.Handlers; +enum HttpServerHandlerActionEvent +{ + ServerStarting, + ServerStarted, + SetupRouter, + ContextBagCreated, + HttpRequestOpen, + HttpRequestClose, + Exception, + Stopping, + Stopped, +} + internal class HttpServerHandlerRepository { private readonly HttpServer parent; @@ -23,17 +36,22 @@ internal class HttpServerHandlerRepository public HttpServerHandlerRepository(HttpServer parent) { this.parent = parent; - this.RegisterHandler(this._default); + RegisterHandler(_default); } public void RegisterHandler(HttpServerHandler handler) { - this.handlers.Add(handler); + handlers.Add(handler); } - private void CallEvery(Action action) + private bool IsEventBreakable(HttpServerHandlerActionEvent eventName) + => eventName == HttpServerHandlerActionEvent.ServerStarting + || eventName == HttpServerHandlerActionEvent.ServerStarted + || eventName == HttpServerHandlerActionEvent.SetupRouter; + + private void CallEvery(Action action, HttpServerHandlerActionEvent eventName) { - Span hspan = CollectionsMarshal.AsSpan(this.handlers); + Span hspan = CollectionsMarshal.AsSpan(handlers); ref HttpServerHandler hpointer = ref MemoryMarshal.GetReference(hspan); for (int i = 0; i < hspan.Length; i++) { @@ -45,22 +63,22 @@ private void CallEvery(Action action) } catch (Exception ex) { - if (!this.parent.ServerConfiguration.ThrowExceptions) + if (parent.ServerConfiguration.ThrowExceptions == false && IsEventBreakable(eventName) == false) { - this.parent.ServerConfiguration.ErrorsLogsStream?.WriteException(ex); + parent.ServerConfiguration.ErrorsLogsStream?.WriteException(ex); } else throw; } } } - internal void ServerStarting(HttpServer val) => this.CallEvery(handler => handler.InvokeOnServerStarting(val)); - internal void ServerStarted(HttpServer val) => this.CallEvery(handler => handler.InvokeOnServerStarted(val)); - internal void SetupRouter(Router val) => this.CallEvery(handler => handler.InvokeOnSetupRouter(val)); - internal void ContextBagCreated(TypedValueDictionary val) => this.CallEvery(handler => handler.InvokeOnContextBagCreated(val)); - internal void HttpRequestOpen(HttpRequest val) => this.CallEvery(handler => handler.InvokeOnHttpRequestOpen(val)); - internal void HttpRequestClose(HttpServerExecutionResult val) => this.CallEvery(handler => handler.InvokeOnHttpRequestClose(val)); - internal void Exception(Exception val) => this.CallEvery(handler => handler.InvokeOnException(val)); - internal void Stopping(HttpServer val) => this.CallEvery(handler => handler.InvokeOnServerStopping(val)); - internal void Stopped(HttpServer val) => this.CallEvery(handler => handler.InvokeOnServerStopped(val)); + internal void ServerStarting(HttpServer val) => CallEvery(handler => handler.InvokeOnServerStarting(val), HttpServerHandlerActionEvent.ServerStarting); + internal void ServerStarted(HttpServer val) => CallEvery(handler => handler.InvokeOnServerStarted(val), HttpServerHandlerActionEvent.ServerStarted); + internal void SetupRouter(Router val) => CallEvery(handler => handler.InvokeOnSetupRouter(val), HttpServerHandlerActionEvent.SetupRouter); + internal void ContextBagCreated(TypedValueDictionary val) => CallEvery(handler => handler.InvokeOnContextBagCreated(val), HttpServerHandlerActionEvent.ContextBagCreated); + internal void HttpRequestOpen(HttpRequest val) => CallEvery(handler => handler.InvokeOnHttpRequestOpen(val), HttpServerHandlerActionEvent.HttpRequestOpen); + internal void HttpRequestClose(HttpServerExecutionResult val) => CallEvery(handler => handler.InvokeOnHttpRequestClose(val), HttpServerHandlerActionEvent.HttpRequestClose); + internal void Exception(Exception val) => CallEvery(handler => handler.InvokeOnException(val), HttpServerHandlerActionEvent.Exception); + internal void Stopping(HttpServer val) => CallEvery(handler => handler.InvokeOnServerStopping(val), HttpServerHandlerActionEvent.Stopping); + internal void Stopped(HttpServer val) => CallEvery(handler => handler.InvokeOnServerStopped(val), HttpServerHandlerActionEvent.Stopped); } diff --git a/src/Http/Hosting/PortableConfigurationBuilder.cs b/src/Http/Hosting/PortableConfigurationBuilder.cs index c801d0e..d021d35 100644 --- a/src/Http/Hosting/PortableConfigurationBuilder.cs +++ b/src/Http/Hosting/PortableConfigurationBuilder.cs @@ -7,8 +7,8 @@ // File name: PortableConfigurationBuilder.cs // Repository: https://github.com/sisk-http/core -using Sisk.Core.Internal.ServiceProvider; using System.ComponentModel; +using Sisk.Core.Internal.ServiceProvider; namespace Sisk.Core.Http.Hosting; @@ -26,44 +26,44 @@ public sealed class PortableConfigurationBuilder internal PortableConfigurationBuilder(HttpServerHostContext context) { - this._context = context; + _context = context; } internal void Build() { - if (this._createIfDontExists && !File.Exists(this._filename)) + if (_createIfDontExists && !File.Exists(_filename)) { - File.Create(this._filename).Close(); + File.Create(_filename).Close(); } - ConfigurationContext provider = new ConfigurationContext(this._filename, this._context, this._context.ServerConfiguration.ListeningHosts[0]); + ConfigurationContext provider = new ConfigurationContext(_filename, _context, _context.ServerConfiguration.ListeningHosts[0]); - var pipelineReader = this._pipeline ?? new JsonConfigParser(); + var pipelineReader = _pipeline ?? new JsonConfigParser(); pipelineReader.ReadConfiguration(provider); - if (this._initializerHandler != null) - this._initializerHandler(this._context.Parameters); + if (_initializerHandler != null) + _initializerHandler(_context.Parameters); - this._context.Parameters.MakeReadonly(); + _context.Parameters.MakeReadonly(); } /// /// Defines an custom configuration pipeline to the builder. /// /// The object. - public PortableConfigurationBuilder WithConfigurationReader(IConfigurationReader reader) + public PortableConfigurationBuilder WithConfigReader(IConfigurationReader reader) { - this._pipeline = reader; + _pipeline = reader; return this; } /// /// Defines an custom configuration pipeline to the builder. /// - /// The type. - public PortableConfigurationBuilder WithConfigurationPipeline() where TPipeline : IConfigurationReader, new() + /// The type. + public PortableConfigurationBuilder WithConfigReader() where TReader : IConfigurationReader, new() { - this._pipeline = new TPipeline(); + _pipeline = new TReader(); return this; } @@ -74,8 +74,8 @@ public PortableConfigurationBuilder WithConfigurationReader(IConfigurationReader /// Optional. Determines if the configuration file should be created if it doens't exists. public PortableConfigurationBuilder WithConfigFile(string filename, bool createIfDontExists = false) { - this._filename = Path.GetFullPath(filename); - this._createIfDontExists = createIfDontExists; + _filename = Path.GetFullPath(filename); + _createIfDontExists = createIfDontExists; return this; } @@ -85,7 +85,7 @@ public PortableConfigurationBuilder WithConfigFile(string filename, bool createI /// The handler of . public PortableConfigurationBuilder WithParameters(Action handler) { - this._initializerHandler = handler; + _initializerHandler = handler; return this; } diff --git a/src/Http/Streams/HttpWebSocket.cs b/src/Http/Streams/HttpWebSocket.cs index 321c96f..7579d80 100644 --- a/src/Http/Streams/HttpWebSocket.cs +++ b/src/Http/Streams/HttpWebSocket.cs @@ -43,6 +43,11 @@ public sealed class HttpWebSocket /// public HttpStreamPingPolicy PingPolicy => this.pingPolicy; + /// + /// Gets or sets the maximum wait time for synchronous listener methods like . + /// + public TimeSpan WaitTimeout { get; set; } = TimeSpan.FromSeconds(60); + /// /// Gets or sets the maximum number of attempts to send a failed message before the server closes the connection. Set it to -1 to /// don't close the connection on failed attempts. @@ -174,9 +179,33 @@ internal async void ReceiveTask() /// Configures the ping policy for this instance of HTTP Web Socket. /// /// The method that runs on the ping policy for this HTTP Web Socket. - public void WithPing(Action act) + public HttpWebSocket WithPing(Action act) { act(this.pingPolicy); + return this; + } + + /// + /// Configures the ping policy for this instance of HTTP Web Socket. + /// + /// The payload/probe message that is sent to the client. + /// The sending interval for each probe message. + public HttpWebSocket WithPing(string probeMessage, TimeSpan interval) + { + this.PingPolicy.DataMessage = probeMessage; + this.PingPolicy.Interval = interval; + this.PingPolicy.Start(); + return this; + } + + /// + /// Sends an text message to the remote point. + /// + /// The target message which will be as an encoded UTF-8 string. + public void Send(object? message) + { + string? t = message?.ToString(); + this.Send(t ?? string.Empty); } /// @@ -325,10 +354,25 @@ public void WaitForClose() /// Null is returned if a connection error is thrown. /// public WebSocketMessage? WaitNext() + { + return this.WaitNext(this.WaitTimeout); + } + + /// + /// Blocks the current thread and waits the next incoming message from this web socket instance within + /// the maximum defined timeout. + /// + /// The maximum time to wait until the next message. + /// + /// Null is returned if a connection error is thrown. + /// + public WebSocketMessage? WaitNext(TimeSpan timeout) { this.waitNextEvent.Reset(); this.isWaitingNext = true; - this.waitNextEvent.WaitOne(); + + this.waitNextEvent.WaitOne(timeout); + return this.lastMessage; } } diff --git a/src/Routing/Route.cs b/src/Routing/Route.cs index 11f665c..cec1394 100644 --- a/src/Routing/Route.cs +++ b/src/Routing/Route.cs @@ -7,8 +7,8 @@ // File name: Route.cs // Repository: https://github.com/sisk-http/core -using Sisk.Core.Entity; using System.Text.RegularExpressions; +using Sisk.Core.Entity; namespace Sisk.Core.Routing { @@ -36,7 +36,7 @@ public class Route /// /// Gets an boolean indicating if this action return is an asynchronous . /// - public bool IsAsync { get => this.isReturnTypeTask; } + public bool IsAsync { get => isReturnTypeTask; } /// /// Gets or sets how this route can write messages to log files on the server. @@ -65,15 +65,15 @@ public string Path { get { - return this.path; + return path; } set { - if (this.UseRegex && this.routeRegex != null) + if (UseRegex && routeRegex != null) { - this.routeRegex = null; + routeRegex = null; } - this.path = value; + path = value; } } @@ -87,10 +87,10 @@ public string Path /// public RouteAction? Action { - get => this._callback; + get => _callback; set { - this._callback = value; + _callback = value; if (value != null) { var memberInfo = value.Method; @@ -102,7 +102,7 @@ public RouteAction? Action } else if (retType.IsAssignableTo(typeof(Task))) { - this.isReturnTypeTask = true; + isReturnTypeTask = true; if (retType.GenericTypeArguments.Length == 0) { throw new InvalidOperationException(string.Format(SR.Route_Action_AsyncMissingGenericType, this)); @@ -138,9 +138,9 @@ public RouteAction? Action /// The function that is called after the route is matched with the request. public Route(RouteMethod method, string path, RouteAction action) { - this.Method = method; + Method = method; this.path = path; - this.Action = action; + Action = action; } /// @@ -153,11 +153,11 @@ public Route(RouteMethod method, string path, RouteAction action) /// The RequestHandlers to run before the route's Action. public Route(RouteMethod method, string path, string? name, RouteAction action, IRequestHandler[]? beforeCallback) { - this.Method = method; + Method = method; this.path = path; - this.Name = name; - this.Action = action; - this.RequestHandlers = beforeCallback ?? Array.Empty(); + Name = name; + Action = action; + RequestHandlers = beforeCallback ?? Array.Empty(); } /// @@ -165,7 +165,7 @@ public Route(RouteMethod method, string path, string? name, RouteAction action, /// public Route() { - this.path = "/"; + path = "/"; } /// @@ -173,14 +173,7 @@ public Route() /// public override string ToString() { - if (string.IsNullOrEmpty(this.Name)) - { - return $"{{Method = {this.Method}, Path = {this.Path}}}"; - } - else - { - return $"{{Method = {this.Method}, Path = {this.Path}, Name={this.Name}}}"; - } + return $"[{Method.ToString().ToUpper()} {path}] {Name ?? Action?.Method.Name}"; } } diff --git a/src/Sisk.Core.csproj b/src/Sisk.Core.csproj index 2ff12bc..5034d18 100644 --- a/src/Sisk.Core.csproj +++ b/src/Sisk.Core.csproj @@ -29,9 +29,9 @@ git http-server,http,web framework,event sources,web sockets - 1.0 - 1.0 - 1.0 + 1.0.2 + 1.0.2 + 1.0.2 LICENSE.txt False