Każda przeglądarka na świecie wysyła Accept-Encoding: gzip, deflate przy każdym żądaniu. Do tej pory serwery HTTP sgcWebSockets ignorowały ten nagłówek i wysyłały treść bez kompresji. Twój HTML, twój CSS, twój JavaScript i twój JSON szły przez sieć w pełnym rozmiarze, mimo że klient przy każdym pojedynczym żądaniu informował, że chętnie rozpakuje mniejszą.
sgcWebSockets 2026.7 dodaje do serwerów HTTP kompresję odpowiedzi. Gdy klient zgłasza obsługę gzip lub deflate, serwer może teraz skompresować odpowiedź przed jej wysłaniem. Działa to dla plików statycznych serwowanych z DocumentRoot oraz dla odpowiedzi budowanych samodzielnie w procedurze obsługi zdarzenia. Funkcja jest domyślnie wyłączona, więc aktualizacja niczego nie zmienia, dopóki jej nie włączysz.
Włączanie kompresji
Kompresję konfiguruje się przez pojedynczy obiekt opcji, HTTPCompression, dostępny w serwerze. W komponencie TsgcWebSocketHTTPServer jest to właściwość published, więc możesz ją ustawić w Object Inspector na etapie projektowania albo w kodzie:
uses
sgcWebSocket;
begin
sgcWebSocketHTTPServer1.HTTPCompression.Enabled := True;
sgcWebSocketHTTPServer1.Active := True;
end;
To cała konfiguracja. Przy Enabled := True serwer sprawdza nagłówek Accept-Encoding każdego żądania i jeśli klient obsługuje jeden ze skonfigurowanych algorytmów, kompresuje treść odpowiedzi i odpowiednio ustawia Content-Encoding. Dodaje też Vary: Accept-Encoding, dzięki czemu pamięci podręczne i serwery proxy stojące przed twoim serwerem uwzględniają kodowanie w kluczu zamiast podawać skompresowaną treść klientowi, który nie potrafi jej odczytać.
Jeśli klient nie zgłasza obsługi kompresji, nic się nie dzieje i odpowiedź wychodzi dokładnie tak jak wcześniej. Kompresja nigdy nie jest narzucana klientowi, który o nią nie poprosił.
Właściwość Algorithms jest zbiorem i oba jej elementy są domyślnie włączone:
sgcWebSocketHTTPServer1.HTTPCompression.Algorithms := [caGZip, caDeflate];
// or gzip only, if you prefer to keep it to a single well-understood encoding
sgcWebSocketHTTPServer1.HTTPCompression.Algorithms := [caGZip];
Gdy klient obsługuje oba, serwer preferuje gzip.
Co jest kompresowane, a co nie
Nie każdą odpowiedź warto kompresować. O tym, które warto, decyduje lista ContentTypes, która ma rozsądne wartości domyślne: text/*, application/json, application/javascript, application/xml, application/xhtml+xml oraz image/svg+xml. Wpisy kończące się na /* dopasowują całą rodzinę typów, więc text/* obejmuje HTML, CSS, zwykły tekst i resztę.
Tekst, HTML, CSS, JavaScript i JSON kompresują się bardzo dobrze, ponieważ są pełne powtarzających się tokenów, nazw znaczników i białych znaków. Formaty już skompresowane, przeciwnie. Obraz JPEG, PNG czy WebP, archiwum ZIP, plik .gz albo film MP4 mają już wyciśniętą nadmiarowość, a przepuszczenie ich ponownie przez zlib tylko zużywa procesor, żeby otrzymać treść mniej więcej tego samego rozmiaru, czasem nawet nieco większą. Tych typów treści nie ma na liście, więc są pomijane.
Listę możesz zastąpić własną:
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;
Sprawdzenie ignoruje przyrostek z zestawem znaków, więc odpowiedź z Content-Type: text/html; charset=utf-8 nadal pasuje do text/*. W serwerze działa też zasada podwójnego zabezpieczenia: jeśli skompresowana treść nie okaże się mniejsza od oryginału, kompresja zostaje odrzucona i wysyłana jest zwykła treść. Nie możesz więc wysłać więcej bajtów, niż wysłałbyś bez niej.
Poziom kompresji i minimalny rozmiar
Level to zwykły poziom zlib, od 1 do 9, domyślnie 6. Ta wartość domyślna to standardowy kompromis między szybkością a stopniem kompresji i jest to wartość, na której pracuje niemal każdy serwer WWW na świecie. Podniesienie jej do 9 kosztuje zauważalnie więcej czasu procesora przy każdej pojedynczej odpowiedzi, a w przypadku JSON rzadko daje dużo więcej: największy zysk względem nieskompresowanej treści pojawia się już na poziomie 1, a poziomy od 6 do 9 walczą o ostatnie kilka procent. Jeśli twój serwer jest ograniczony mocą procesora, zejście do 3 lub 4 jest znacznie ciekawszym eksperymentem niż podniesienie do 9.
MinSize ma domyślną wartość 1024 bajtów, a treści mniejsze od niej są wysyłane bez zmian. Strumień gzip niesie własny nagłówek i stopkę, a poniżej mniej więcej kilobajta narzut ramkowania wraz z kosztem procesora po prostu nie jest wart garstki zaoszczędzonych bajtów. Drobne potwierdzenia JSON i krótkie przekierowania trafiają do tego koszyka i tak właśnie powinno być.
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;
Co na tym zyskujesz
Zysk widać w sieci, a nie w kodzie. Strona HTML czy odpowiedź API w formacie JSON są bardzo powtarzalne, więc typowy ładunek o rozmiarze kilkudziesięciu kilobajtów zwykle kurczy się kilkukrotnie, zanim opuści serwer. Mniej bajtów w sieci oznacza mniej wymian pakietów potrzebnych do ich dostarczenia i właśnie tu tkwi prawdziwy zysk: na połączeniach mobilnych i na łączach o dużym opóźnieniu czas dostarczenia odpowiedzi zależy głównie od liczby wymian pakietów, a nie od przepustowości. Treść, która mieści się w mniejszej liczbie pakietów, po prostu dociera szybciej.
Kosztem jest czas procesora na serwerze, raz na odpowiedź, i przy domyślnym poziomie jest on niewielki. W przypadku witryny z dużą ilością tekstu albo API zwracającego JSON ten kompromis niemal zawsze się opłaca.
Odpowiedzi budowane samodzielnie
W procedurach obsługi zdarzeń nie musisz robić nic specjalnego. Zbuduj odpowiedź tak jak zawsze, ustaw ContentText lub ContentStream oraz ContentType, a serwer skompresuje ją przy wysyłce, jeśli klient poprosił o kompresję i typ treści znajduje się na liście:
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;
Krok kompresji wykonuje się po zakończeniu twojej procedury obsługi, więc Content-Length, które widzi klient, jest długością po kompresji, a Content-Encoding zostaje ustawione za ciebie. Jeśli sam ustawisz ContentEncoding, bo serwujesz treść już skompresowaną, serwer całkowicie zostawi twoją odpowiedź w spokoju.
Dwa rodzaje odpowiedzi celowo nigdy nie są kompresowane: strumienie Server-Sent Events (text/event-stream), których nie wolno buforować, oraz odpowiedzi bez treści, takie jak 204 i 304.
Pliki serwowane z DocumentRoot
Pliki statyczne są traktowane tak samo, i to bez żadnego kodu. Wskaż serwerowi folder, włącz kompresję, a pliki HTML, CSS, JavaScript, SVG i JSON znajdujące się w nim będą kompresowane przy wysyłce. Obrazy i archiwa w tym samym folderze już nie, ponieważ ich typów treści nie ma na liście.
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
Serwer HTTP.sys
Serwer HTTP.sys dla Windows, TsgcWebSocketServer_HTTPAPI, ma ten sam obiekt opcji HTTPCompression, z tymi samymi właściwościami i tymi samymi wartościami domyślnymi. Jest jedna różnica, którą warto opisać precyzyjnie: w serwerze HTTP.sys ta właściwość jest public, a nie published, więc nie pojawia się w Object Inspector. Musisz ją ustawić w kodzie, zanim aktywujesz serwer:
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;
Na razie tylko HTTP/1.1
W tym wydaniu kompresowane są tylko odpowiedzi HTTP/1.1. Odpowiedzi HTTP/2 i HTTP/3 pozostają nietknięte. Jeśli serwujesz swoją witrynę przez HTTP/2, ta funkcja na razie niczego u ciebie nie zmienia. Warto powiedzieć to jasno, zamiast pozwolić ci włączyć właściwość i zastanawiać się, dlaczego nic się nie zmniejszyło.
Dostępność
Kompresja odpowiedzi HTTP jest dostępna w sgcWebSockets 2026.7 dla Delphi 7 do 13 oraz C++Builder, na Win32, Win64, Linux64 i macOS. Jest domyślnie wyłączona: po aktualizacji nic w twoich istniejących serwerach się nie zmienia, dopóki nie ustawisz HTTPCompression.Enabled := True.
Klienci z aktywną subskrypcją mogą pobrać nową wersję ze strefy klienta. Użytkownicy wersji próbnej mogą pobrać zaktualizowany instalator ze strony esegece.com/products/websockets/download.
Pytania, uwagi albo pomoc w dostrojeniu typów treści dla twojego serwera? Skontaktuj się z nami, odpowiedź otrzymasz od osób, które napisały ten kod.
