Compressão gzip e deflate no servidor HTTP do sgcWebSockets | Blog eSeGeCe

Compressão gzip e deflate no servidor HTTP do sgcWebSockets

· Componentes

Todo navegador do planeta envia Accept-Encoding: gzip, deflate em cada requisição. Até agora, os servidores HTTP do sgcWebSockets ignoravam esse cabeçalho e enviavam o corpo sem comprimir. Seu HTML, seu CSS, seu JavaScript e seu JSON saíam pela rede em tamanho integral, mesmo com o cliente avisando você, em cada requisição, que teria todo o prazer em descompactar uma resposta menor.

O sgcWebSockets 2026.7 adiciona compressão de respostas aos servidores HTTP. Quando o cliente anuncia gzip ou deflate, o servidor agora pode comprimir a resposta antes de enviá-la. Funciona para arquivos estáticos servidos a partir de DocumentRoot e para respostas que você mesmo monta em um manipulador de evento. Vem desativado por padrão, então atualizar a versão não muda nada até que você o ative.

Ativando

A compressão é configurada através de um único objeto de opções, HTTPCompression, no servidor. Em TsgcWebSocketHTTPServer ele é uma propriedade publicada, então você pode defini-lo no Object Inspector em tempo de design, ou por código:

uses
  sgcWebSocket;

begin
  sgcWebSocketHTTPServer1.HTTPCompression.Enabled := True;
  sgcWebSocketHTTPServer1.Active := True;
end;

É toda a configuração necessária. Com Enabled := True o servidor olha o cabeçalho Accept-Encoding de cada requisição e, se o cliente suportar um dos algoritmos configurados, comprime o corpo da resposta e define Content-Encoding de acordo. Ele também adiciona Vary: Accept-Encoding, para que caches e proxies à frente do seu servidor considerem a codificação em vez de entregar um corpo comprimido em gzip a um cliente que não consegue lê-lo.

Se o cliente não anunciar compressão, nada acontece e a resposta sai exatamente como antes. A compressão nunca é imposta a um cliente que não a pediu.

A propriedade Algorithms é um conjunto, e ambos os membros vêm habilitados por padrão:

sgcWebSocketHTTPServer1.HTTPCompression.Algorithms := [caGZip, caDeflate];

// or gzip only, if you prefer to keep it to a single well-understood encoding
sgcWebSocketHTTPServer1.HTTPCompression.Algorithms := [caGZip];

Quando o cliente suporta os dois, o servidor prefere gzip.

O que é comprimido, e o que não é

Nem toda resposta vale a pena comprimir. A lista ContentTypes decide quais valem, e ela vem com um padrão sensato: text/*, application/json, application/javascript, application/xml, application/xhtml+xml e image/svg+xml. As entradas terminadas em /* correspondem a uma família inteira, então text/* cobre HTML, CSS, texto puro e o restante.

Texto, HTML, CSS, JavaScript e JSON comprimem muito bem, porque são cheios de tokens repetidos, nomes de tags e espaços em branco. Formatos já comprimidos, não. Um JPEG, um PNG, uma imagem WebP, um arquivo ZIP, um arquivo .gz ou um vídeo MP4 já tiveram sua redundância espremida, e passá-los novamente pelo zlib apenas gasta CPU para produzir um corpo de tamanho praticamente igual, às vezes até um pouco maior. Esses tipos de conteúdo não estão na lista, portanto são ignorados.

Você pode substituir a lista pela sua própria:

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;

A verificação ignora qualquer sufixo de charset, então uma resposta com Content-Type: text/html; charset=utf-8 continua correspondendo a text/*. Há também uma regra de segurança extra no servidor: se o corpo comprimido não sair menor do que o original, a compressão é descartada e o corpo puro é enviado. Você nunca acaba enviando mais bytes do que enviaria sem ela.

Nível de compressão e tamanho mínimo

Level é o nível usual do zlib, de 1 a 9, e o padrão é 6. Esse padrão é o compromisso clássico entre velocidade e taxa de compressão, e é o valor com que praticamente todo servidor web do mundo funciona. Elevá-lo para 9 custa bem mais CPU em cada resposta e, para JSON, raramente compensa: o grande ganho em relação ao corpo sem compressão já acontece no nível 1, e os níveis de 6 a 9 disputam os últimos poucos por cento. Se o seu servidor está limitado por CPU, baixar para 3 ou 4 é um experimento bem mais interessante do que subir para 9.

MinSize tem 1024 bytes como padrão, e corpos menores do que isso são enviados como estão. Um fluxo gzip carrega seu próprio cabeçalho e rodapé e, abaixo de um kilobyte mais ou menos, o custo de enquadramento somado ao custo de CPU simplesmente não compensa o punhado de bytes economizados. Pequenas confirmações em JSON e redirecionamentos curtos caem nessa categoria, e é assim que deve ser.

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;

O que você ganha com isso

O ganho aparece na rede, não no seu código. Uma página HTML ou a resposta de uma API JSON é altamente repetitiva, então uma carga típica de algumas dezenas de kilobytes normalmente comprime várias vezes antes de sair do servidor. Menos bytes na rede significa menos idas e voltas para entregá-los, e é aí que a coisa realmente compensa: em conexões móveis e em enlaces de alta latência, o tempo para entregar uma resposta é dominado pelo número de idas e voltas, não pela largura de banda. Um corpo que cabe em menos pacotes simplesmente chega antes.

O custo é CPU no servidor, uma vez por resposta, e é pequeno no nível padrão. Para um site cheio de texto ou uma API JSON, a troca quase sempre vale a pena.

Respostas que você mesmo monta

Você não precisa fazer nada de especial nos seus manipuladores de evento. Monte a resposta como sempre fez, defina ContentText ou ContentStream e o ContentType, e o servidor a comprime na saída se o cliente pediu compressão e o tipo de conteúdo está na lista:

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;

A etapa de compressão roda depois que o seu manipulador retorna, então o Content-Length que o cliente vê é o comprimento comprimido, e Content-Encoding é definido para você. Se você definir ContentEncoding por conta própria, porque está servindo um corpo que já está comprimido, o servidor deixa sua resposta completamente intacta.

Dois tipos de resposta nunca são comprimidos, deliberadamente: os fluxos Server-Sent Events (text/event-stream), que não podem ser bufferizados, e as respostas sem corpo, como 204 e 304.

Arquivos servidos a partir de DocumentRoot

Os arquivos estáticos recebem o mesmo tratamento, sem código nenhum. Aponte o servidor para uma pasta, habilite a compressão, e os arquivos HTML, CSS, JavaScript, SVG e JSON dentro dela são comprimidos na saída. As imagens e os arquivos compactados na mesma pasta não são, porque seus tipos de conteúdo não estão na lista.

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

O servidor HTTP.sys

O servidor HTTP.sys do Windows, TsgcWebSocketServer_HTTPAPI, tem o mesmo objeto de opções HTTPCompression, com as mesmas propriedades e os mesmos padrões. Há uma diferença que vale a pena deixar clara: no servidor HTTP.sys a propriedade é public em vez de published, então ela não aparece no Object Inspector. Você precisa defini-la por código, antes de ativar o servidor:

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;

Somente HTTP/1.1, por enquanto

Nesta versão, apenas as respostas HTTP/1.1 são comprimidas. As respostas HTTP/2 e HTTP/3 não são tocadas. Se você serve o seu site sobre HTTP/2, este recurso ainda não muda nada para você. É melhor deixar isso claro do que permitir que você habilite uma propriedade e depois se pergunte por que nada encolheu.

Disponibilidade

A compressão de respostas HTTP está disponível no sgcWebSockets 2026.7 para Delphi 7 até 13 e C++Builder, em Win32, Win64, Linux64 e macOS. Vem desativada por padrão: nada muda nos seus servidores existentes ao atualizar, até que você defina HTTPCompression.Enabled := True.

Os clientes com assinatura ativa podem baixar a nova versão na área de clientes. Os usuários de avaliação podem obter o instalador atualizado em esegece.com/products/websockets/download.

Dúvidas, comentários ou ajuda para ajustar os tipos de conteúdo do seu servidor? Fale conosco, você receberá uma resposta das pessoas que escreveram o código.