Table of Contents

Manual (advanced) setup

Use manual setup when you need to assemble the server pieces yourself, such as when one process must expose multiple hosts, ports, routers, or custom server configuration. For most applications, the builder API is shorter and should be preferred. Manual setup is useful when you want direct control over the four core pieces: a Router, one or more ListeningHost objects, an HttpServerConfiguration, and the final HttpServer.

First, we need to understand the request/response concept. It is quite simple: for every request, there must be a response. Sisk follows this principle as well. Let's create a method that responds with a "Hello, World!" message in HTML, specifying the status code and headers.

// Program.cs
using Sisk.Core.Http;
using Sisk.Core.Routing;

static HttpResponse IndexPage(HttpRequest request)
{
    HttpResponse indexResponse = new HttpResponse
    {
        Status = System.Net.HttpStatusCode.OK,
        Content = new HtmlContent(@"
            <html>
                <body>
                    <h1>Hello, world!</h1>
                </body>
            </html>
        ")
    };

    return indexResponse;
}

The next step is to associate this method with an HTTP route.

Routers

Routers are abstractions of request routes and serve as the bridge between requests and responses for the service. Routers manage service routes, functions, and errors.

A router can have several routes, and each route can perform different operations on that path, such as executing a function, serving a page, or providing a resource from the server.

Let's create our first router and associate our IndexPage method with the index path.

Router mainRouter = new Router();

mainRouter.MapGet("/", IndexPage);

Now our router can receive requests and send responses. However, mainRouter is not tied to a host or a server, so it will not work on its own. The next step is to create our ListeningHost.

Listening Hosts and Ports

A ListeningHost can host a router and multiple listening ports for the same router. A ListeningPort is a prefix where the HTTP server will listen.

Here, we can create a ListeningHost that points to two endpoints for our router:

ListeningHost myHost = new ListeningHost
{
    Router = mainRouter,
    Ports = new ListeningPort[]
    {
        new ListeningPort("http://localhost:5000/")
    }
};

Now our HTTP server will listen to the specified endpoints and redirect its requests to our router.

Server Configuration

Server configuration is responsible for most of the behavior of the HTTP server itself. In this configuration, we can associate ListeningHosts with our server.

HttpServerConfiguration config = new HttpServerConfiguration();
config.ListeningHosts.Add(myHost); // Add our ListeningHost to this server configuration

Common server configuration options:

Property Default Use when Notes
RemoteRequestsAction RequestListenAction.Accept The service should reject non-local requests unless they come through a trusted reverse proxy. Set to Drop only when your deployment topology is clear.
IncludeRequestIdHeader false Clients or proxies need the Sisk request id in the X-Request-Id response header. Pair with logs that include HttpRequest.RequestId.
IdleConnectionTimeout 120 seconds Idle keep-alive connections should be closed sooner or later. This is applied by the HTTP engine.
NormalizeHeadersEncodings false You receive headers with an encoding mismatch. This has a processing cost; leave it disabled unless needed.
SendSiskHeader true You want to hide or expose the X-Powered-By Sisk header. Disable it for stricter production header policies.
OptionsLogMode LogOutput.Both You want to reduce or redirect logs generated by automatic OPTIONS handling. Uses the same log mode values as routes.
AsyncRequestProcessing true You need deterministic single-request processing for diagnostics. Disabling it limits throughput.
DisposeDisposableContextValues true Request bag values that implement IDisposable should be disposed automatically. Keep enabled unless ownership is managed elsewhere.
ConvertIAsyncEnumerableIntoEnumerable true Value handlers should receive async enumerables as blocking enumerable values. Disable when you implement your own async-stream handling.
KeepAlive true Connections should remain reusable after responses. Disable for clients or intermediaries that do not handle persistent connections well.
ForceTrailingSlash false GET routes should redirect to a trailing-slash URL. Applies only to non-regex routes.
MaximumContentLength 0 Request bodies need a size limit. 0 means unlimited until framework or memory limits are reached.
EnableAutomaticResponseCompression false Responses should be compressed automatically when the client supports it. Existing CompressedContent responses are not compressed again.

Next, we can create our HTTP server:

HttpServer server = new HttpServer(config);
server.Start();    // Starts the server
Console.ReadKey(); // Prevents the application from exiting

Now we can compile our executable and run our HTTP server with the command:

dotnet watch

At runtime, open your browser and navigate to the server path, and you should see: