Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
CypherPotato committed Apr 8, 2024
1 parent e02e0d8 commit be9d275
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 10 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,12 @@ Sisk can do web development the way you want. Create MVC, MVVC, SOLID applicatio

Sisk is a highly modular and sustainable framework designed for creating a variety of applications, including Restful applications, gRPC, Websockets, file servers, GraphQL, Entity Framework, and more. Its development is ongoing, with a focus on simplicity, easy maintenance, and an enjoyable experience for developers. Sisk is known for its efficiency, even in low-performance environments, and it can handle over twenty thousand requests per second. The framework is compatible with any machine supporting .NET, including those that do not require Native AOT. Sisk also offers additional implementations and packages for extended functionality.

While Sisk draws inspiration from ASP.NET, it aims for simpler and more performant development without the need for installing additional components. It provides a straightforward and robust development model, allowing developers to handle requests efficiently and explicitly. Sisk simplifies HTTP-related tasks and offers comprehensive documentation and open-source access to its source code, making it accessible and easy to learn for web developers.
While Sisk draws inspiration from ASP.NET, it aims for simpler and more performant development without the need for installing additional components. It provides a straightforward and robust development model, allowing developers to handle requests efficiently and explicitly. Sisk simplifies HTTP-related tasks and offers comprehensive documentation and open-source access to its source code, making it accessible and easy to learn for web developers.

## Stargazers over time

[![Stargazers over time](https://starchart.cc/sisk-http/core.svg?variant=light)](https://starchart.cc/sisk-http/core)

## License

The entire Sisk ecosystem is licensed under the [MIT License](https://sisk.project-principium.dev/license).
18 changes: 16 additions & 2 deletions src/Http/Hosting/HttpServerHostContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ namespace Sisk.Core.Http.Hosting;
/// Represents the class that hosts most of the components needed to run a Sisk application.
/// </summary>
/// <definition>
/// public class HttpServerHostContext
/// public class HttpServerHostContext : IDisposable
/// </definition>
/// <type>
/// Class
/// </type>
public class HttpServerHostContext
public class HttpServerHostContext : IDisposable
{
/// <summary>
/// Gets the initialization parameters from the portable configuration file.
Expand Down Expand Up @@ -154,4 +154,18 @@ public async Task StartAsync(bool verbose = true, bool preventHault = true)
{
await Task.Run(() => Start(verbose, preventHault));
}

/// <summary>
/// Invalidates this class and releases the resources used by it, and permanently closes the HTTP server.
/// </summary>
/// <definition>
/// public void Dispose()
/// </definition>
/// <type>
/// Method
/// </type>
public void Dispose()
{
HttpServer.Dispose();
}
}
76 changes: 71 additions & 5 deletions src/Http/HttpServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace Sisk.Core.Http
/// </summary>
/// <definition>
/// public class HttpServer : IDisposable
/// </definition>
/// </definition>
/// <type>
/// Class
/// </type>
Expand Down Expand Up @@ -58,6 +58,10 @@ public partial class HttpServer : IDisposable
internal HttpWebSocketConnectionCollection _wsCollection = new HttpWebSocketConnectionCollection();
internal List<string>? listeningPrefixes;
internal HttpServerHandlerRepository handler;

internal AutoResetEvent waitNextEvent = new AutoResetEvent(false);
internal bool isWaitingNextEvent = false;
internal HttpServerExecutionResult? waitingExecutionResult;

static HttpServer()
{
Expand All @@ -74,7 +78,7 @@ static HttpServer()
/// public static HttpServerHostContext CreateBuilder(Action{{HttpServerHostContextBuilder}} handler)
/// </definition>
/// <type>
/// Static method
/// Static method
/// </type>
public static HttpServerHostContext CreateBuilder(Action<HttpServerHostContextBuilder> handler)
{
Expand All @@ -83,14 +87,30 @@ public static HttpServerHostContext CreateBuilder(Action<HttpServerHostContextBu
return builder.Build();
}

/// <summary>
/// Builds an empty <see cref="HttpServerHostContext"/> context with predefined listening port.
/// </summary>
/// <definition>
/// public static HttpServerHostContext CreateBuilder(ushort port)
/// </definition>
/// <type>
/// Static method
/// </type>
public static HttpServerHostContext CreateBuilder(ushort port)
{
var builder = new HttpServerHostContextBuilder();
builder.UseListeningPort(port);
return builder.Build();
}

/// <summary>
/// Builds an empty <see cref="HttpServerHostContext"/> context.
/// </summary>
/// <definition>
/// public static HttpServerHostContext CreateBuilder()
/// </definition>
/// <type>
/// Static method
/// Static method
/// </type>
public static HttpServerHostContext CreateBuilder()
{
Expand All @@ -111,7 +131,7 @@ public static HttpServerHostContext CreateBuilder()
/// public static HttpServer Emit(in ushort insecureHttpPort, out HttpServerConfiguration configuration, out ListeningHost host, out Router router)
/// </definition>
/// <type>
/// Static method
/// Static method
/// </type>
public static HttpServer Emit(
in ushort insecureHttpPort,
Expand Down Expand Up @@ -281,7 +301,7 @@ public HttpServer(HttpServerConfiguration configuration)
/// </summary>
/// <param name="obj">The instance of the server handler.</param>
/// <definition>
/// public void RegisterHandler(HttpServerHandler obj)
/// public void RegisterHandler(HttpServerHandler obj)
/// </definition>
/// <type>
/// Method
Expand All @@ -291,6 +311,52 @@ public void RegisterHandler(HttpServerHandler obj)
handler.RegisterHandler(obj);
}

/// <summary>
/// Waits for the next execution result from the server. This method obtains the next completed context from the HTTP server,
/// both with the request and its response. This method does not interrupt the asynchronous processing of requests.
/// </summary>
/// <remarks>
/// Calling this method, it starts the HTTP server if it ins't started yet.
/// </remarks>
/// <definition>
/// public HttpServerExecutionResult WaitNext()
/// </definition>
/// <type>
/// Method
/// </type>
public HttpServerExecutionResult WaitNext()
{
if (!IsListening)
Start();
if (isWaitingNextEvent)
throw new InvalidOperationException(SR.Httpserver_WaitNext_Race_Condition);

waitingExecutionResult = null;
isWaitingNextEvent = true;
waitNextEvent.WaitOne();

return waitingExecutionResult!;
}

/// <summary>
/// Waits for the next execution result from the server asynchronously. This method obtains the next completed context from the HTTP server,
/// both with the request and its response. This method does not interrupt the asynchronous processing of requests.
/// </summary>
/// <remarks>
/// Calling this method, it starts the HTTP server if it ins't started yet.
/// </remarks>
/// <definition>
/// public async Task{{HttpServerExecutionResult}} WaitNextAsync()
/// </definition>
/// <type>
/// Method
/// </type>
public async Task<HttpServerExecutionResult> WaitNextAsync()
{
return await Task.Run(WaitNext);
}


/// <summary>
/// Restarts this HTTP server, sending all processing responses and starting them again, reading the listening ports again.
/// </summary>
Expand Down
11 changes: 9 additions & 2 deletions src/Http/HttpServer__Core.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ private async Task ProcessRequest(HttpListenerContext context)
// determines the content type
baseResponse.ContentType = resHeaders[HttpKnownHeaderNames.ContentType] ?? response.Content.Headers.ContentType?.ToString();

// determines if the response should be sent as chunked or normal
// determines if the response should be sent as chunked or normal
if (response.SendChunked)
{
baseResponse.SendChunked = true;
Expand Down Expand Up @@ -441,7 +441,7 @@ response.Content is StreamContent ||
catch (Exception)
{
baseResponse.Abort();
}
}
}

if (OnConnectionClose != null)
Expand Down Expand Up @@ -492,6 +492,13 @@ response.Content is StreamContent ||
ServerConfiguration.AccessLogsStream?.WriteLine(line);
}

if (isWaitingNextEvent)
{
waitingExecutionResult = executionResult;
waitNextEvent.Set();
isWaitingNextEvent = false;
}

if (!flag.AsyncRequestProcessing)
{
Monitor.Exit(httpListener);
Expand Down
21 changes: 21 additions & 0 deletions src/Internal/HttpStringInternals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// File name: HttpStringInternals.cs
// Repository: https://github.com/sisk-http/core

using Sisk.Core.Routing;
using System.Collections.Specialized;
using System.Runtime.CompilerServices;

Expand All @@ -23,6 +24,11 @@ public static bool PathRouteMatch(ReadOnlySpan<char> routeA, ReadOnlySpan<char>
const string ROUTE_GROUP_START = "<";
const string ROUTE_GROUP_END = ">";

if (routeA == Route.AnyPath || routeB == Route.AnyPath)
{
return true;
}

int pathPatternSepCount = routeA.Count(SEPARATOR);
int reqsPatternSepCount = routeB.Count(SEPARATOR);

Expand Down Expand Up @@ -69,6 +75,11 @@ public static PathMatchResult IsPathMatch(ReadOnlySpan<char> pathPattern, ReadOn
const string ROUTE_GROUP_START = "<";
const string ROUTE_GROUP_END = ">";

if (pathPattern == Route.AnyPath)
{
return new PathMatchResult(true, null);
}

NameValueCollection? query = null;

int pathPatternSepCount = pathPattern.Count(SEPARATOR);
Expand Down Expand Up @@ -113,6 +124,11 @@ public static PathMatchResult IsPathMatch(ReadOnlySpan<char> pathPattern, ReadOn
#else
public static bool PathRouteMatch(string routeA, string routeB, bool ignoreCase)
{
if (routeA == Route.AnyPath || routeB == Route.AnyPath)
{
return true;
}

string[] routeAP = routeA.Split('/', StringSplitOptions.RemoveEmptyEntries);
string[] routeBP = routeB.Split('/', StringSplitOptions.RemoveEmptyEntries);

Expand Down Expand Up @@ -146,6 +162,11 @@ public static bool PathRouteMatch(string routeA, string routeB, bool ignoreCase)
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
public static PathMatchResult IsPathMatch(string pathPattern, string requestPath, bool ignoreCase)
{
if (pathPattern == Route.AnyPath)
{
return new PathMatchResult(true, null);
}

NameValueCollection? query = null;

string[] pathPatternParts = pathPattern.Split('/', StringSplitOptions.RemoveEmptyEntries);
Expand Down
1 change: 1 addition & 0 deletions src/Internal/SR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ static class SR
public const string Httpserver_StartMessage = "The HTTP server is listening at:";
public const string Httpserver_Commons_HeaderAfterContents = "Cannot send headers or status code after sending response contents.";
public const string Httpserver_Commons_RouterTimeout = "Request maximum execution time exceeded it's limit.";
public const string Httpserver_WaitNext_Race_Condition = "This HTTP server is already waiting for the next request in another call of this method.";

public const string HttpStatusCode_IllegalStatusCode = "The HTTP status code must be three-digits long.";
public const string HttpStatusCode_IllegalStatusReason = "The HTTP reason phrase must be equal or smaller than 8192 characters.";
Expand Down
11 changes: 11 additions & 0 deletions src/Routing/Route.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ public class Route
internal bool isReturnTypeTask;
internal Regex? routeRegex;
private string path;

/// <summary>
/// Represents an route path which captures any URL path.
/// </summary>
/// <definition>
/// public const string AnyPath = "**RouteAnyPath";
/// </definition>
/// <type>
/// Constant
/// </type>
public const string AnyPath = "/<<ANY>>";

/// <summary>
/// Gets or sets an <see cref="TypedValueDictionary"/> for this route, which can hold contextual variables
Expand Down

0 comments on commit be9d275

Please sign in to comment.