# Cadente

Source: https://docs.sisk-framework.org/de/docs/cadente.html

Cadente ist eine experimentelle verwaltete HTTP/1.1-Listener-Implementierung für Sisk. Sie dient als Ersatz für den Standard-`System.Net.HttpListener` und bietet mehr Kontrolle und Flexibilität, insbesondere auf Nicht‑Windows‑Plattformen.

## Überblick

Standardmäßig verwendet Sisk `HttpListener` (aus `System.Net`) als zugrunde liegende HTTP‑Server‑Engine. Während `HttpListener` unter Windows stabil und performant ist (wo es den Kernel‑Modus‑Treiber HTTP.sys nutzt), ist seine Implementierung unter Linux und macOS verwaltet und hatte historisch gesehen Einschränkungen, wie fehlende native SSL‑Unterstützung (die einen Reverse‑Proxy wie Nginx erfordert) und unterschiedliche Leistungsmerkmale.

Cadente zielt darauf ab, diese Probleme zu lösen, indem es einen vollständig verwalteten HTTP/1.1‑Server in C# bereitstellt. Seine Hauptziele sind:

- **Native SSL Support:** Funktioniert auf allen Plattformen, ohne externe Proxies oder komplexe Konfiguration zu benötigen.
- **Cross-Platform Consistency:** Identisches Verhalten unter Windows, Linux und macOS.
- **Performance:** Entwickelt, um eine Hochleistungs‑Alternative zum verwalteten `HttpListener` zu sein.
- **Independence:** Entkoppelt von `System.Net.HttpListener`, wodurch Sisk vor möglichen zukünftigen Deprecations oder fehlender Wartung dieser Komponente in .NET geschützt wird.

> [!WARNING]
> **Experimenteller Status**
> 
> Cadente befindet sich derzeit in einer experimentellen Phase (Beta). Es wird noch nicht für kritische Produktionsumgebungen empfohlen. Die API und das Verhalten können sich ändern.

## Installation

Cadente ist als separates Paket verfügbar. Um es mit Sisk zu verwenden, benötigen Sie das Paket `Sisk.Cadente.CoreEngine`.

```bash
dotnet add package Sisk.Cadente.CoreEngine
```

`Sisk.Cadente` und `Sisk.Cadente.CoreEngine` zielen auf .NET 9 und .NET 10 ab. Sie können nicht in Projekten verwendet werden, die .NET 8 anvisieren, obwohl `Sisk.HttpServer` dies unterstützt.

## Verwendung mit Sisk

Um Cadente als HTTP‑Engine für Ihre Sisk‑Anwendung zu verwenden, müssen Sie den `HttpServer` so konfigurieren, dass er `CadenteHttpServerEngine` anstelle der Standard‑Engine nutzt.

`CadenteHttpServerEngine` adaptiert den Cadente-`HttpHost` an die von Sisk benötigte `HttpServerEngine`‑Abstraktion.

```csharp
using Sisk.Core.Http;
using Sisk.Cadente.CoreEngine;

using var host = HttpServer.CreateBuilder()
    .UseEngine<CadenteHttpServerEngine>()
    .UseSsl(certificate: CertificateHelper.CreateTrustedDevelopmentCertificate("localhost"))
    .Build();

await host.StartAsync();
```

Wenn ein lauschernder Host mit SSL konfiguriert ist, gibt [HttpServer.ListeningPrefixes](https://docs.sisk-framework.org/api/Sisk.Core.Http.HttpServer.ListeningPrefixes.md) seine Adressen mit dem `https`‑Schema aus. Sendet ein Client plain HTTP an einen mit SSL konfigurierten Port, antwortet Cadente mit einer `301 Moved Permanently`‑Weiterleitung zur `https`‑Adresse desselben Hosts und Pfads.

### Erweiterte Konfiguration

Sie können die zugrunde liegende `HttpHost`‑Instanz anpassen, indem Sie dem Konstruktor von `CadenteHttpServerEngine` eine Setup‑Aktion übergeben. Dies ist nützlich, um Timeouts oder andere Low‑Level‑Einstellungen zu konfigurieren.

```csharp
using var engine = new CadenteHttpServerEngine(host =>
{
    // Konfiguriere Lese-/Schreib-Timeouts des Clients
    host.TimeoutManager.ClientReadTimeout = TimeSpan.FromSeconds(30);
    host.TimeoutManager.ClientWriteTimeout = TimeSpan.FromSeconds(30);
});
```

## Eigenständige Nutzung

Obwohl Cadente hauptsächlich für Sisk konzipiert ist, kann es als eigenständiger HTTP‑Server verwendet werden (ähnlich wie `HttpListener`).

```csharp
using Sisk.Cadente;

var host = new HttpHost(15000)
{
    Handler = new MyHostHandler()
};

host.Start();
Thread.Sleep(-1);

class MyHostHandler : HttpHostHandler
{
    public override async Task OnContextCreatedAsync(HttpHost host, HttpHostContext context)
    {
        context.Response.StatusCode = 200;

        using var stream = await context.Response.GetResponseStreamAsync(chunked: true);
        using var writer = new StreamWriter(stream);
        await writer.WriteLineAsync("Hello, world!");
    }
}
```

Wenn der Antwortkörper klein und bereits im Speicher ist, schreiben `WriteInlineContent` und `WriteInlineContentAsync` die Header und den Körper in einem einzigen Vorgang. Die Antwort muss einen `Content-Length`‑Header besitzen:

```csharp
byte[] body = "Hello, world!"u8.ToArray();

context.Response.StatusCode = 200;
context.Response.Headers.Set(new HttpHeader("Content-Length", body.Length.ToString()));
await context.Response.WriteInlineContentAsync(body);
```

## Anforderungsvalidierung

Cadente prüft das Request‑Framing, bevor es den Handler erreicht. Seit Version 1.7 tut es Folgendes:

- unterstützt pipelined Anfragen bei Keep‑Alive‑Verbindungen;
- verwirft Anfragemethoden, die keine gültigen HTTP‑Tokens sind, und Anforderungsziele, die Bytes außerhalb des sichtbaren ASCII‑Bereichs (`%x21-7E`) enthalten;
- verwirft Anfragen, deren `Transfer-Encoding` etwas anderes als einen einzelnen `chunked`‑Wert ist, sowie Anfragen mit widersprüchlichen `Content-Length`‑Headern;
- antwortet mit `431 Request Header Fields Too Large` und schließt die Verbindung, wenn die Request‑Zeile und Header 8 KB überschreiten.

Ungültige Anfragen werden mit einem Fehler beantwortet und die Verbindung wird geschlossen, ohne den Handler aufzurufen.
