Skip to content

v1.3

Latest
Compare
Choose a tag to compare
@CypherPotato CypherPotato released this 13 Nov 18:49
· 1 commit to main since this release

v.1.3

Sisk 1.3 is being released today! This update brings performance improvements, another way to write your favorite APIs, and something similar to dependency injection.

Sisk's commitment remains the same: to be simple to develop a quality HTTP application. Sisk has no dependencies other than the .NET ecosystem itself, and all its code is still less than 1KB in total. Sisk has a minimal footprint, focusing performance on your application's layer, not the server.

We would also like to showcase the new logo of the project. It is more modern while still referencing what the project used to be.

Sisk logo

Performance improvements

Sisk has a limitation: it depends on the current implementation of HttpListener to work. This implementation is great on Windows and has performance comparable to ASP.NET, but in recent tests, we found that the managed implementation of HttpListener lags significantly behind ASP.NET's Kestrel, which happens outside Windows environments. Even so, Sisk still manages to be much more performant and efficient than many other development frameworks in other languages. Our goal is not to be better than ASP.NET, but the closer we get to its performance, the more viable the choice of Sisk becomes for more projects.

In version 1.3, we achieved an average performance increase of 15% overall for request processing. This is because we improved the way the server sends the content of a response to the client. The HttpResponse object depends on HttpContent to define content to send to the client, and previously, this object had to be serialized through HttpContent.ReadAsByteArrayAsync. The problem is that many contents are based on ByteArrayContent, and when calling this method on this type of content, a memory buffer was created, and the response bytes were copied to this buffer. Then, this buffer was copied to the response's output stream.

Thanks to UnsafeAccessor in .NET 8, we can access the response bytes directly, without the need to create this secondary buffer. This greatly improved overall performance.

benchmark

In addition, there were performance improvements outside of the request processor as well. Improvements were made in the way the server writes messages to the access log and deserializes multipart content.

Static HttpContexts

An experimental feature is to expose the current HttpContext of a request in a local context of the execution thread. This allows you to expose members of a request outside the request method, greatly improving the readability of your code. The code snippet below shows an abstract class that functions as an API controller with an embedded database.

public abstract class Controller : RouterModule
{
    public DbContext Database
    {
        // Create or get an new DbContext instance
        get => HttpContext.Current.RequestBag.GetOrAdd(() => new DbContext());
    }

    // Exposing the HttpRequest instance is supported too
    public HttpRequest Request { get => HttpContext.Current.Request; }

    protected override void OnSetup(Router parentRouter)
    {
        base.OnSetup(parentRouter);

        HasRequestHandler(RequestHandler.Create(
            execute: (req, ctx) =>
            {
                // disposes the current DbContext if used
                ctx.RequestBag.GetOrDefault<DbContext>()?.Dispose();
                return null;
            },
            executionMode: RequestHandlerExecutionMode.AfterResponse));
    }
}

And use this implementation with methods that do not have an HttpRequest parameter, as you already have a Request in your controller instance.

[RoutePrefix("/api/posts")]
public class PostsController : Controller
{
    [RouteGet]
    public IEnumerable<Blog> ListPosts()
    {
        return Database.Posts
            .Where(post => post.AuthorId == AuthenticatedUser.Id)
            .ToList();
    }

    [RouteGet("<id>")]
    public Post GetPost()
    {
        int blogId = Request.RouteParameters["id"].GetInteger();

        Post? post = Database.Posts
            .FirstOrDefault(post => post.Id == blogId && post.AuthorId == AuthenticatedUser.Id);

        return post ?? new HttpResponse(404);
    }
}

You can read more about this feature in the documentation.

Full changelog

New features:

  • New logo.
  • Added support for .NET 9.
  • Added the StringKeyStore.SetRange method.
  • Added the TypedValueDictionary.GetOrDefault<T> method.
  • Added the TypedValueDictionary.GetOrAdd<T> method.
  • Added the TypedValueDictionary.GetOrAddAsync<T> method.
  • Added the HttpRequest.Uri property.
  • Added the HttpServerExecutionResult.Elapsed property.
  • Added the HttpContext.Current static property.
  • Added the HttpResponseExtensions.WithHeader(StringKeyStore) method.
  • Added the MimeHelper.DefaultMimeType static property.
  • Added the MimeHelper.IsBrowserKnownInlineMimeType(string) method.
  • Added the HttpServerFlags.PreventResponseContentsInProhibitedMethods flag.
  • Added the Route.Get, Route.Post, Route.Put, Route.Delete, Route.Head, Route.Options, Route.Any and Route.Patch static methods.
  • Added the RouterModule.OnSetup virtual method.
  • Added the CookieHelper static class.

Changes:

  • Improved the path matching algorithm.
  • Improved the log writer algorithm.
  • The HTTP server will now show an warning when it's starting without any route defined.
  • Changed type from Router.Action from RouteAction to Delegate.
  • Changed return type from TypedValueDictionary.Unset from void to bool.
  • Fixed when the HTTP server was starting with one random port and defined ports when using portable configurations.

Fixes:

  • Fixed the TypedValueDictionary.IsSet<T> attribute usage.
  • Fixed an issue where byte counts were being calculated with decimals in SizeHelper.HumanReadableSize.

Breaking changes:

  • Removed the HttpKnownHeaderNames.From member.
  • Removed the HttpRequest.GetQueryValue methods.
  • Removed the HttpServer.GetVersion() method. You can still use the HttpServer.PoweredBy property instead.
  • Route/paths parameters will not be added into HttpRequest.Query anymore. You should use the HttpRequest.RouteParameters property instead.
  • Made CookieHelper and static class and removed it from all classes who used it. You still can use the SetCookie methods from where it was defined.
  • Deprecated the HttpServerConfiguration.DefaultEncoding property.

Thank you for using Sisk!