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