Jeder Browser auf diesem Planeten sendet bei jeder Anfrage Accept-Encoding: gzip, deflate. Bisher haben die HTTP Server von sgcWebSockets diesen Header ignoriert und den Body unkomprimiert verschickt. Ihr HTML, Ihr CSS, Ihr JavaScript und Ihr JSON gingen in voller Größe über die Leitung, obwohl der Client Ihnen bei jeder einzelnen Anfrage mitteilte, dass er eine kleinere Antwort gerne auspacken würde.
sgcWebSockets 2026.7 ergänzt die HTTP Server um die Komprimierung von Antworten. Wenn der Client gzip oder deflate anbietet, kann der Server die Antwort jetzt vor dem Senden komprimieren. Das funktioniert für statische Dateien aus DocumentRoot und für Antworten, die Sie selbst in einem Ereignishandler erzeugen. Die Funktion ist standardmäßig deaktiviert, ein Update ändert also nichts, bis Sie sie einschalten.
Einschalten
Die Komprimierung wird über ein einziges Optionsobjekt am Server konfiguriert, HTTPCompression. Bei TsgcWebSocketHTTPServer ist es eine published Property, Sie können sie also zur Entwurfszeit im Objektinspektor setzen oder im Code:
uses
sgcWebSocket;
begin
sgcWebSocketHTTPServer1.HTTPCompression.Enabled := True;
sgcWebSocketHTTPServer1.Active := True;
end;
Das ist die gesamte Einrichtung. Mit Enabled := True prüft der Server bei jeder Anfrage den Accept-Encoding Header, und wenn der Client einen der konfigurierten Algorithmen unterstützt, komprimiert er den Antwort-Body und setzt Content-Encoding entsprechend. Zusätzlich setzt er Vary: Accept-Encoding, damit Caches und Proxys vor Ihrem Server nach der Kodierung unterscheiden und keinen mit gzip komprimierten Body an einen Client ausliefern, der ihn nicht lesen kann.
Wenn der Client keine Komprimierung anbietet, passiert nichts und die Antwort geht genau so hinaus wie zuvor. Komprimierung wird niemals einem Client aufgezwungen, der nicht danach gefragt hat.
Die Property Algorithms ist eine Menge, und beide Elemente sind standardmäßig aktiviert:
sgcWebSocketHTTPServer1.HTTPCompression.Algorithms := [caGZip, caDeflate];
// or gzip only, if you prefer to keep it to a single well-understood encoding
sgcWebSocketHTTPServer1.HTTPCompression.Algorithms := [caGZip];
Wenn der Client beide unterstützt, bevorzugt der Server gzip.
Was komprimiert wird und was nicht
Nicht jede Antwort lohnt eine Komprimierung. Die Liste ContentTypes entscheidet, welche es sind, und sie kommt mit einer sinnvollen Vorgabe: text/*, application/json, application/javascript, application/xml, application/xhtml+xml und image/svg+xml. Einträge, die auf /* enden, treffen auf eine ganze Familie zu, text/* deckt also HTML, CSS, reinen Text und den Rest ab.
Text, HTML, CSS, JavaScript und JSON lassen sich sehr gut komprimieren, weil sie voller wiederholter Token, Tag-Namen und Leerräume sind. Bereits komprimierte Formate nicht. Ein JPEG, ein PNG, ein WebP-Bild, ein ZIP-Archiv, eine .gz-Datei oder ein MP4-Video hat seine Redundanz schon verloren, und es noch einmal durch zlib zu schicken verbrennt nur CPU, um einen Body von ungefähr derselben Größe zu erzeugen, manchmal sogar einen etwas größeren. Diese Content Types stehen nicht in der Liste und werden daher übersprungen.
Sie können die Liste durch Ihre eigene ersetzen:
uses
sgcWebSocket, sgcWebSocket_Classes;
begin
with sgcWebSocketHTTPServer1.HTTPCompression do
begin
ContentTypes.Clear;
ContentTypes.Add('text/*'); // html, css, plain text
ContentTypes.Add('application/json'); // your API replies
ContentTypes.Add('application/javascript');
ContentTypes.Add('image/svg+xml');
ContentTypes.Add('application/x-ndjson'); // whatever else you serve
Enabled := True;
end;
sgcWebSocketHTTPServer1.Active := True;
end;
Die Prüfung ignoriert ein angehängtes Charset, eine Antwort mit Content-Type: text/html; charset=utf-8 passt also weiterhin auf text/*. Im Server gibt es außerdem eine doppelte Absicherung: Wenn der komprimierte Body nicht kleiner ausfällt als das Original, wird die Komprimierung verworfen und der unveränderte Body gesendet. Sie können also nie mehr Bytes verschicken, als Sie es ohne die Komprimierung getan hätten.
Komprimierungsstufe und Mindestgröße
Level ist die übliche zlib-Stufe von 1 bis 9, und die Vorgabe ist 6. Diese Vorgabe ist der übliche Kompromiss zwischen Geschwindigkeit und Kompressionsrate, und es ist der Wert, mit dem nahezu jeder Webserver der Welt läuft. Eine Anhebung auf 9 kostet bei jeder einzelnen Antwort messbar mehr CPU, und bei JSON bringt sie selten viel zusätzlich: Der große Gewinn gegenüber dem unkomprimierten Body entsteht schon bei Stufe 1, und die Stufen 6 bis 9 streiten sich um die letzten paar Prozent. Wenn Ihr Server CPU-gebunden ist, ist der Weg nach unten auf 3 oder 4 ein weitaus interessanteres Experiment als der Weg nach oben auf 9.
MinSize ist auf 1024 Bytes voreingestellt, und kleinere Bodys werden unverändert gesendet. Ein gzip-Stream trägt seinen eigenen Header und Trailer mit sich, und unterhalb von etwa einem Kilobyte lohnen der Overhead des Framings und die CPU-Kosten die Handvoll gesparter Bytes einfach nicht. Winzige JSON-Bestätigungen und kurze Redirects fallen in diese Kategorie, und das ist auch gut so.
with sgcWebSocketHTTPServer1.HTTPCompression do
begin
Level := 6; // 1..9, default 6
MinSize := 1024; // bytes, default 1024, smaller bodies are sent as-is
Enabled := True;
end;
Was Ihnen das bringt
Der Gewinn zeigt sich auf der Leitung, nicht in Ihrem Code. Eine HTML-Seite oder die Antwort einer JSON-API ist stark repetitiv, eine typische Nutzlast von einigen zehn Kilobyte schrumpft daher meist um ein Mehrfaches, bevor sie den Server verlässt. Weniger Bytes auf der Leitung bedeuten weniger Roundtrips, um sie dorthin zu bringen, und genau dort zahlt es sich wirklich aus: Bei Mobilfunkverbindungen und Strecken mit hoher Latenz wird die Zeit bis zur Auslieferung einer Antwort von der Anzahl der Roundtrips bestimmt, nicht von der Bandbreite. Ein Body, der in weniger Pakete passt, kommt schlicht früher an.
Der Preis ist CPU auf dem Server, einmal pro Antwort, und er ist bei der voreingestellten Stufe gering. Für eine textlastige Website oder eine JSON-API lohnt sich der Handel fast immer.
Antworten, die Sie selbst erzeugen
Sie müssen in Ihren Ereignishandlern nichts Besonderes tun. Bauen Sie die Antwort so auf, wie Sie es immer getan haben, setzen Sie ContentText oder ContentStream und den ContentType, und der Server komprimiert sie auf dem Weg nach draußen, sofern der Client Komprimierung angefragt hat und der Content Type in der Liste steht:
procedure TForm1.sgcWebSocketHTTPServer1CommandGet(aConnection: TsgcWSConnection;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
if ARequestInfo.Document = '/api/orders' then
begin
AResponseInfo.ContentType := 'application/json';
AResponseInfo.ContentText := GetOrdersAsJSON; // your payload, plain text
// nothing else to do. If the client sent Accept-Encoding: gzip and the
// body is at least MinSize bytes, it goes out gzipped.
end;
end;
Der Komprimierungsschritt läuft, nachdem Ihr Handler zurückgekehrt ist, die Content-Length, die der Client sieht, ist also die komprimierte Länge, und Content-Encoding wird für Sie gesetzt. Wenn Sie ContentEncoding selbst setzen, weil Sie einen bereits komprimierten Body ausliefern, lässt der Server Ihre Antwort vollständig unangetastet.
Zwei Arten von Antworten werden bewusst nie komprimiert: Server-Sent-Events-Streams (text/event-stream), die nicht gepuffert werden dürfen, und Antworten ohne Body wie 204 und 304.
Dateien aus DocumentRoot
Statische Dateien bekommen dieselbe Behandlung, ganz ohne Code. Richten Sie den Server auf einen Ordner, schalten Sie die Komprimierung ein, und die HTML-, CSS-, JavaScript-, SVG- und JSON-Dateien darin werden auf dem Weg nach draußen komprimiert. Die Bilder und die Archive im selben Ordner nicht, weil ihre Content Types nicht in der Liste stehen.
sgcWebSocketHTTPServer1.HTTPUploadFiles.DocumentRoot := 'C:\www';
sgcWebSocketHTTPServer1.HTTPCompression.Enabled := True;
sgcWebSocketHTTPServer1.Active := True;
// GET /index.html -> compressed, text/html is in ContentTypes
// GET /app.js -> compressed
// GET /logo.png -> not compressed, image/png is not in the list
// GET /favicon.ico -> not compressed
Der HTTP.sys Server
Der HTTP.sys Server von Windows, TsgcWebSocketServer_HTTPAPI, hat dasselbe Optionsobjekt HTTPCompression, mit denselben Properties und denselben Vorgaben. Es gibt einen Unterschied, den man genau benennen sollte: Beim HTTP.sys Server ist die Property public statt published, sie erscheint also nicht im Objektinspektor. Sie müssen sie im Code setzen, bevor Sie den Server aktivieren:
uses
sgcWebSocket_Server_HTTPAPI;
begin
sgcWebSocketServer_HTTPAPI1.HTTPCompression.Enabled := True;
sgcWebSocketServer_HTTPAPI1.HTTPCompression.Level := 6;
sgcWebSocketServer_HTTPAPI1.HTTPCompression.MinSize := 1024;
sgcWebSocketServer_HTTPAPI1.Active := True;
end;
Vorerst nur HTTP/1.1
In dieser Version werden nur Antworten über HTTP/1.1 komprimiert. Antworten über HTTP/2 und HTTP/3 werden nicht angefasst. Wenn Sie Ihre Website über HTTP/2 ausliefern, ändert diese Funktion für Sie vorerst nichts. Das sollte klar gesagt werden, statt Sie eine Property einschalten zu lassen und Sie sich dann wundern zu lassen, warum nichts geschrumpft ist.
Verfügbarkeit
Die Komprimierung von HTTP-Antworten ist in sgcWebSockets 2026.7 für Delphi 7 bis 13 und C++Builder verfügbar, auf Win32, Win64, Linux64 und macOS. Sie ist standardmäßig deaktiviert: An Ihren bestehenden Servern ändert das Update nichts, bis Sie HTTPCompression.Enabled := True setzen.
Kunden mit einem aktiven Abonnement können den neuen Build im Kundenbereich herunterladen. Testbenutzer finden den aktualisierten Installer unter esegece.com/products/websockets/download.
Fragen, Feedback oder Hilfe beim Abstimmen der Content Types für Ihren eigenen Server? Kontaktieren Sie uns, Sie erhalten eine Antwort von den Leuten, die den Code geschrieben haben.
