ASP.NET Core SignalR hinter Nginx: Fix für „Requests with Connection: Upgrade cannot have content in the request body“

ASP.NET Core SignalR hinter Nginx: Fix für „Requests with Connection: Upgrade cannot have content in the request body“

ASP.NET Core bringt mit Kestrel einen minimalistischen Webserver mit. Dieser setzt den Fokus darauf, dynamische mit ASP.NET Core generierte Seiten möglichst effizient auszuliefern. Ganz anders sah es mit dem alten ASP.NET aus: Hier musste der sehr umfängliche IIS eingesetzt werden. Dies hatte Vor- und Nachteile. Der IIS war zwar recht komplex, konnte dafür aber mit einem vollumfänglichen Webserver wie dem Apache2 mithalten. Ein zweiter Webserver als Reverse Proxy war hier daher oft nicht notwendig.

Anders sieht es mit Kestrel aus. Da dieser im Design eines Microservices recht schlank gehalten ist, fehlen ihm selbst viele Funktionen, die andere leichtgewichtige und auf Leistung optimierte Webserver wie Nginx mitbringen. Dies ist nicht zwingend schlecht. Kestrel ist für einfache Anwendungen ausreichend und bringt keinen unnötigen Ballast mit. Wer mehr benötigt, kann einen Nginx oder auch Apache-Webserver davor schalten. Somit hat man ein modulares Design, das auf unnötigen Ballast verzichtet.

Für viele Anwendungsfälle dürfte die Kombination von Kestrel mit Nginx ein guter Kompromiss sein zwischen mehr Funktionen, aber zeitgleich weniger Overhead als ein threadbasierter Apache-Webserver. Im Falle von Nginx ist mir bei der Verwendung von Websockets mit der SignalR-Bibliothek ein Problem aufgefallen.

Der „Connection“-Header

Mein anfängliches Setup war ein großteils vanilla konfigurierter Nginx-Webserver. Dieser sollte alle Anfragen an den Kestrel-Webserver einer ASP.NET Core Webanwendung weiterleiten, ausgenommen ein bestimmtes Verzeichnis. Um Tipparbeit zu sparen kopierte ich mir hierfür ein Snippet kopiert und angepasst:

server {
    listen       80;
    server_name  _;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        
        proxy_set_header Connection "upgrade";
    }

    location /forum {
        proxy_pass http://127.0.0.1:81;
    }
}

Abgesehen von SignalR funktionierte dies auch. Bei den dafür notwendigen Websocket-Verbindungen wurde folgender Fehler geworden:

Connection id "XXX" bad request data: "Requests with 'Connection: Upgrade' cannot have content in the request body."

Das Problem liegt im Connection-Header, der hart auf upgrade gesetzt wurde. Stattdessen muss er auf $http_connection gesetzt werden. Dies entspricht dem mitgelieferten Wert aus der Anfrage. Somit muss der Location-Block wie folgt lauten:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    
    # https://stackoverflow.com/questions/46556780/asp-net-core-2-0-unable-to-post-to-database
    proxy_set_header Connection $http_connection;
}

Icon von Freepik/www.flaticon.com

Leave a Reply