Skip to content

Releases: sisk-http/core

v1.6.0

16 Dec 19:27

Choose a tag to compare

This update brings significant improvements to HTTP engine architecture, a new file server system, and framework enhancements.

HTTP File Server

Introduced the new HttpFileServer system for serving static files efficiently and flexibly:

  • Optimized streaming with Range header support for audio and video
  • Customizable converters through HttpFileServerFileConverter
  • Configurable directory listing
  • Automatic content negotiation based on MIME types
var route = HttpFileServer.CreateServingRoute("/static", "./public");
router += route;

// Or with custom configuration
var handler = new HttpFileServerHandler("./public") {
    AllowDirectoryListing = true,
    // Add custom converters
};
var route = HttpFileServer.CreateServingRoute("/files", handler);

The new FileContent class allows returning files with automatic streaming and range request support:

router.SetRoute(RouteMethod.Get, "/download", request => {
    return new FileContent("./video.mp4");
});

The HttpFileAudioConverter and HttpFileVideoConverter converters provide automatic support for range requests in media files, enabling seeking in audio/video players.

HTTP Engine Improvements

Significant improvements to HTTP engine abstraction:

  • Nova interface HttpServerEngineContext: Abstração completa do ciclo de vida de requisições HTTP
  • HttpServerEngineWebSocket: Interface abstrata para WebSocket em diferentes engines
  • Mecanismos de event loop: Suporte através de HttpServerEngineContextEventLoopMecanism
  • Melhor integração SSL: Classe ListeningHostSslOptions para configuração SSL por listening host
var host = new ListeningHost("https://localhost:5000") {
    SslOptions = new ListeningHostSslOptions {
        Certificate = myCertificate
    }
};

Logging Improvements

  • RotatingLogPolicyCompressor: Interface for automatic compression of rotated logs
  • GZipRotatingLogPolicyCompressor: Built-in implementation with GZip compression
  • PrefixedLogStream: Improvements to prefixed log stream
var rotatingPolicy = new RotatingLogPolicy {
    Compressor = new GZipRotatingLogPolicyCompressor()
};

HTTP Server Handler Improvements

  • Handler execution ordering: Added Priority property to HttpServerHandler to control execution order
  • HttpServerHandlerSortingComparer: New comparer for sorting handlers by priority (lower values execute first)
  • HttpServerHandlerRepository: Changed internal storage from List<T> to SortedSet<T> for automatic priority-based ordering

WebSocket Improvements

  • Fix: Corrections to WebSocket connection management
  • Improvements to HttpWebSocketConnectionCollection
  • Better handling of closed connections

Other Changes

  • HttpRequestEventSource: Improvements to Server-Sent Events system
  • CertificateHelper: New helper class for SSL certificate management
  • HttpRequest.DisconnectToken: Improvements to disconnection detection
  • HttpServerExecutionResult: New fields for better execution tracking

Breaking Changes

  • ListeningHost: SSL properties moved to ListeningHostSslOptions
  • HttpServerEngine: New interface with additional abstract methods
  • Some method signatures in engines have been changed to support the new contexts

Bug Fixes

  • Fixed: Simplified connection closed check in HTTP request parsing
  • Fixed: HTTP body drainage corrected
  • Fixed: Issues with chunked encoding in requests
  • Fixed: Better abort handling in HTTP components
  • Fixed: Optimized HTTP header parsing and handling

v1.5.2

09 Oct 11:40

Choose a tag to compare

v1.5

18 Aug 13:11

Choose a tag to compare

Contributions

  • ResolveEndpoint optimized so that if an IP address is explicitly specified, that IP address is used by @Sascha-L in #23

HTTP engine abstraction

The Sisk Framework is now independent of HttpListener.

In this update, all HttpListener-dependent mechanisms have been abstracted into what are called HTTP Engines, which control the abstraction level between the framework and the incoming HTTP components.

With this change, Sisk will be able to use other HTTP processors, such as Cadente Project, Kestrel, or other servers.

Each server can bring features not previously supported natively in Sisk, such as SSL, Quic, pipelining, etc.

Currently, Sisk continues to use .NET's HttpListener as it remains the most stable. Follow and contribute to the development of the Cadente engine for Sisk here.

CORS Improvements

In this update, two global constants have been introduced:

  • CrossOriginResourceSharingHeaders.AutoFromRequestMethod
  • CrossOriginResourceSharingHeaders.AutoFromRequestHeaders

The behavior is similar to CrossOriginResourceSharingHeaders.AutoAllowOrigin: the server responds with CORS headers according to the request:

using var host = HttpServer.CreateBuilder()
    .UseCors(new CrossOriginResourceSharingHeaders(
        allowOrigin: CrossOriginResourceSharingHeaders.AutoAllowOrigin,
        allowMethods: [CrossOriginResourceSharingHeaders.AutoFromRequestMethod],
        allowHeaders: [CrossOriginResourceSharingHeaders.AutoFromRequestHeaders],
        exposeHeaders: [HttpKnownHeaderNames.ContentType, "X-Authenticated-Account-Id"],
        allowCredentials: true))
    ...

Web socket API breaking changes

The entire API for the HttpWebSocket class was changed. The motivation for this change was to reduce async/await errors, since the internal WebSocket object doesn't provide a synchronous API. This API rewrite fixes memory leak issues, error handling in asynchronous events, and message pooling.

  • No more synchronous methods or events.
  • All Send() methods were removed. Use SendAsync() overloads instead.
  • No more WaitForExit() methods. Use ReceiveMessageAsync() with a cancellation token or timeout instead.
  • No more Close(). Use CloseAsync() instead.

Read the updated docs for more info.

Bug fixes and other changes

  • Entirely refactored the LogStream code:
    • General improvements and fixes in concurrent writing.
    • Improvements in asynchronous and synchronous processing.
    • Improvements in the shared LogStream singleton.
    • Fixes for Flush and FlushAsync discharge.
    • Fixed a bug that caused the circular buffer to come in reverse.
    • Implemented IAsyncDisposable.
  • General fixes and improvements in CircularBuffer<T>.
  • Added HttpRequest.DisconnectToken. Note: this feature may not be compatible with all HTTP engines.
  • Added the LogMode property for the HttpContext class. With this property, you can decide which log modes are enabled for the current context, which will override the current route log mode.
  • Added the HttpContext.GetCurrentContext() method, which returns the current HttpContext if the current thread is running on a HTTP request context.
  • Added the HttpResponse.GetHeaderValue() helper method, which attempts to get an header value from both defined HttpResponse.Content.Headers property or HttpResponse.Headers.
  • Added the HttpServerConfiguration.DefaultAccessLogFormat constant.
  • Added the TypedValueDictionary.GetValue and TypedValueDictionary.SetValue helper methods.
  • Added the MimeHelper.IsPlainTextMimeType helper method.
  • Added the {:header} constant literal for the log-formatting options.
  • Improved exception-handling for forwarding resolvers.
  • Fixed an issue where the ForwardingResolver methods could throw an exception before the initialization of the HttpRequest object, which caused the HttpServerExecutionResult.HttpRequest to have an null HttpRequest object.
  • Fixed an possible stack overflow issue when using InitializationParameterCollection.ContainsKey.
  • Fixed an issue where the HTTP server were compressing already compressed contents through the EnableAutomaticResponseCompression option.
  • Fixed an issue where the HTTP server were trying to send responses generated by the Router.CallbackErrorHandler, Router.NotFoundErrorHandler, Router.MethodNotAllowedErrorHandler to contexts where was streaming content.
  • Fixed issues where some HTTP headers could be set after sending content on the HttpResponseStreamManager class.
  • Fixed issues in the access log formatter.
  • Fixed an issue where the HTTP server was not enabling error-logging for user-handled errors.

Full Changelog: 1.4.3...1.5.0

v1.4.3

02 May 03:02

Choose a tag to compare

Full Changelog: 1.4.2...1.4.3


Content compression:

  • Added the HttpServerConfiguration.EnableAutomaticResponseCompression property.

Streaming:

  • Removed the GetLinkedCancellationSource method for both HttpRequestEventSource and HttpWebSocket classes which was added in v1.4.2. Sorry for that!

Bug fixes:

  • Fixed an issue where closing web-socket WaitNext methods were not returning after the WS connection being closed.
  • Fixed an issue where multiple WS messages were being discarded when using WaitNext, because they were not enqueued. Now it is enqueued.
  • Fixed an issue where empty WS messages were closing the connection by the server side.
  • Fixed an issue where the HTTP server weren't treating HEAD requests as GET requests to GET routes.
  • Fixed an issue where the HTTP server was allowing sending content in prohibited methods (HEAD and CONNECT).
  • Fixed an issue where the HttpServerExecutionResult.Response were always null.
  • Fixed an issue where the access log were not correctly logging HTTP response status codes.
  • Removed the obsolete HttpServerConfiguration.DefaultCultureInfo property.

v1.4.2

08 Apr 20:45

Choose a tag to compare

Full Changelog: 1.4.1...1.4.2


Multipart objects:

  • Added the MultipartFormCollection.GetFile(string) method.
  • Added the MultipartFormCollection.Files property.
  • Added the MultipartObject.IsFile property.

HTTP Requests:

  • Added the HttpRequest.DefaultJsonSerializerOptions static property.
  • Added the HttpRequest.GetBodyContents method.
  • Added the HttpRequest.GetBodyContentsAsync method.
  • Several improvements to the HttpRequest.GetJsonContent overloads.
  • Several improvements to the HttpRequest.GetFormContentAsync method.

Hosting:

  • Added the InitializationParameterCollection.GetStringValue(string) method.

HTML content:

  • Added the HtmlContent(ReadOnlySpan<byte> contents, Encoding encoding) constructor.
  • Added the HtmlContent(ReadOnlySpan<byte> contents) constructor.

Logging:

  • Added a bunch of async methods, such as WriteLineAsync to LogStream.
  • Added the PrefixedLogStream class.
  • Breaking: the LogStream.WriteException is not virtual anymore.

Streaming:

  • Added the GetLinkedCancellationSource method for both HttpRequestEventSource and HttpWebSocket classes.

Bug fixes:

  • Fixed an issue where IAsyncEnumerable<> and IEnumerable<> results were streaming the results outside the router context, which allowed exceptions to not be handled correctly.
  • Fixed an typo error in the MultipartObject.HasContents property documentation.
  • Fixed an issue where the HTTP server was returning an 400 OK and 500 Ok errors for unhandled exceptions instead the right description codes.

v1.4.1

29 Mar 20:14

Choose a tag to compare

Full Changelog: 1.4.0.1...1.4.1


  • Added the Router.MatchRouteExpression method.
  • Added the Router.IsRouteExpressionsOverlap method.
  • Added the CrossOriginResourceSharingHeaders.AutoAllowOrigin constant.
  • Added the CrossOriginResourceSharingHeaders(string?, string[]?, string[]?, string[]?, string[]?, TimeSpan?) constructor.
  • Added the TypedValueDictionary.GetOrAdd<T> method.
  • Added more mime types to the inline mime type list.
  • Added the [FlagsAttribute] to the RequestHandlerExecutionMode enum.
  • Added the HttpRequest.GetMultipartFormContentAsync method.
  • Added the HttpRequest.GetFormContentAsync method.
  • Added the HttpRequest.GetEventSourceAsync method.
  • Added the HttpRequest.GetWebSocketAsync method.
  • Added the HttpWebSocket.SendAsync method.
  • Added the HttpRequestEventSource.SendAsync method.
  • Added the HttpRequest.GetJsonContent and HttpRequest.GetJsonContentAsync methods.
  • Made the HttpWebSocket and HttpRequestEventSource IDisposable.
  • Fixed the wrong return type for the StringValue.GetSingle() method.
  • Fixed an issue where the log formatter was formatting the %ti constant with the month number instead the date and time minute.
  • Deprecated the HttpServerConfiguration.DefaultCultureInfo property. Use CultureInfo.DefaultThreadCurrentCulture instead.

v1.4

10 Feb 18:22

Choose a tag to compare

Full Changelog: 1.3.2.0...1.4.0.1


v1.4

Goodbye, HttpServerFlags!

Some "advanced" properties of the server, present in the old HttpServerFlags, have been migrated to the HttpServerConfiguration object. This means that the HttpServerFlags class has also been removed, along with all references to it.

See below for the migrated and removed properties:

Property name State Observations
ThrowContentOnNonSemanticMethods ❌ Removed See note 1.
PreventResponseContentsInProhibitedMethods ❌ Removed See note 1.
AsyncRequestProcessing ➡ Migrated
HeaderNameRequestId ❌ Removed
SendCorsHeaders ❌ Removed See note 2.
TreatHeadAsGetMethod ❌ Removed See note 3.
OptionsLogMode ➡ Migrated
SendSiskHeader ➡ Migrated
WebSocketBufferSize ❌ Removed
RequestStreamCopyBufferSize ❌ Removed
NormalizeHeadersEncodings ➡ Migrated
ForceTrailingSlash ➡ Migrated
IdleConnectionTimeout ➡ Migrated
EnableNewMultipartFormReader ❌ Removed See other changes.
ConvertIAsyncEnumerableIntoEnumerable ➡ Migrated

Note 1: This is a overreal design change in Sisk. In summary, the framework will no longer treat the developer like a baby and prevent them from performing illegal operations according to the HTTP RFC. Instead, Sisk will allow it, but the responsibility of following the HTTP fundamentals now lies with the user, not the framework.

Note 2: The server will no longer listen to the SendCorsHeaders property. To not send CORS headers, you can use an empty or null instance of CrossOriginResourceSharingHeaders.Empty in your ListeningHost. Another option is to disable the use of CORS for that route with the Route.UseCors property. Additionally, you can define routes of type RouteMethod.Options to manually handle OPTIONS requests.

Note 3: The HTTP server will no longer treat HEAD requests as GET. You can still define routes with RouteMethod.Head and handle them directly, or use RouteMethod.Get | RouteMethod.Head to handle a route for two different methods, and get the request method with HttpRequest.Method.

Easy compressed contents

Added 3 new HTTP contents:

These contents provides a easy way to compress HTTP contents of a response on-the-fly, like:

router.MapGet("/hello.html", request => {
    string myHtml = "...";
    
    return new HttpResponse () {
        Content = new GZipContent(new HtmlContent(myHtml))
    };
});

You can also extend your own compressing content from the CompressedContent class.

Auto-dispose IDisposable context values

The new DisposeDisposableContextValues property allows values that are IDisposable to be automatically disposed of at the end of an HTTP context, removing the need to create a RequestHandler for this task.

Other changes

  • Added the Router.CheckForRouteCollisions property.
  • Added the StringKeyStore.AsStringValueCollection method.
  • Removed the experimental warning from the HttpContext.Current property.
  • Removed the old Multipart Form parser.
  • Fixed an issue where the served HttpContent wasn't being correctly disposed in certain situations.
  • Deprecated the HttpServerExecutionResult.DnsFailed field.
  • Breaking: removed the HttpServerHostContextBuilder.UseFlags method.
  • Breaking: removed the StringValue.GetParsable<T> method. You should use the StringValue.Get<T> method instead.

v1.3.2

02 Jan 20:51

Choose a tag to compare

  • Fixed a bug where the HTTP server was not binding correctly to prefixes that contained paths on hosts that are not "localhost". - #17
  • Fixed a bug where the router's collision detector was not working correctly when routes had exactly the same definitions.
  • Fixed a bug where [RouteAttribute] attributes adjacent to other attributes were not being correctly assigned to routes

v1.3.1

12 Dec 02:38

Choose a tag to compare

v.1.3.1

  • Breaking: removed the HttpServerConfiguration.DefaultEncoding property. #0c7e36~0f4, at 68
  • Added experimental support for IAsyncEnumerable<T> responses. Can be disabled by disabling the new HttpServerFlags.ConvertIAsyncEnumerableIntoEnumerable flag.
  • Added more constructors for HttpHeaderCollection class. #93f8ad~294, from 30 to 59
  • Added more constructors for StringKeyStore class. #93f8ad~efb, at 51
  • Added more constructors for Router class. #0c7e36~42b, at 93
  • Added the StringKeyStore.AddRange method. #93f8ad~efb, at 292
  • The Multipart object reader now decodes the request payload using the request encoding instead UTF-8. [#93f8ad~294](#93f8ad~294, from 30 to 59)
  • Added the HttpContext.IsRequestContext property. #710492~3a5, at 36
  • Added the HttpRequest.Abort method (again). #710492~89d, at 516
  • Sisk now uses StringComparer.Ordinal in most places which doens't requires invariant comparisons.
  • Other minor changes and improvements.

Sisk.SslProxy

  • Newly trusted certificates are now inserted in the user key store, not the machine key store. #bd9de4~391, from 106 to 107
  • Fixed an issue where the DNS resolver could resolve to an IPv6 address in an deactivated network system. #2e29d1

v1.3

13 Nov 18:49

Choose a tag to compare

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!