sgcWebSockets HTTP サーバーにおける gzip と deflate 圧縮 | eSeGeCe Blog

sgcWebSockets HTTP サーバーにおける gzip と deflate 圧縮

· コンポーネント

世界中のどのブラウザも、すべてのリクエストで Accept-Encoding: gzip, deflate を送信しています。これまで sgcWebSockets の HTTP サーバーはそれを無視し、ボディを圧縮せずに送っていました。HTML も CSS も JavaScript も JSON も、フルサイズのままネットワークに流れていたのです。クライアントはリクエストのたびに「もっと小さいものを喜んで展開しますよ」と伝えていたにもかかわらず、です。

sgcWebSockets 2026.7 は HTTP サーバーにレスポンス圧縮を追加しました。クライアントが gzip または deflate に対応していることを通知すると、サーバーは送信前にレスポンスを圧縮できます。これは DocumentRoot から配信される静的ファイルにも、イベントハンドラーで自分で組み立てるレスポンスにも機能します。既定では無効なので、有効にするまでアップグレードによって何も変わりません。

有効にする

圧縮はサーバー上の単一のオプションオブジェクト HTTPCompression で設定します。TsgcWebSocketHTTPServer では published プロパティなので、デザイン時にオブジェクトインスペクタで設定することも、コードで設定することもできます。

uses
  sgcWebSocket;

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

設定はこれだけです。Enabled := True にすると、サーバーはすべてのリクエストの Accept-Encoding ヘッダーを調べ、クライアントが設定済みのアルゴリズムのいずれかに対応していればレスポンスボディを圧縮し、それに応じて Content-Encoding を設定します。さらに Vary: Accept-Encoding も付加するので、サーバーの前段にあるキャッシュやプロキシがエンコーディングをキーとして扱い、gzip 圧縮されたボディを解凍できないクライアントに渡してしまうことがありません。

クライアントが圧縮を通知しなければ何も起こらず、レスポンスはこれまでとまったく同じように送られます。圧縮を要求していないクライアントに圧縮が強制されることは決してありません。

Algorithms プロパティは集合型で、両方のメンバーが既定で有効です。

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

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

クライアントが両方に対応している場合、サーバーは gzip を優先します。

何が圧縮され、何が圧縮されないか

すべてのレスポンスが圧縮に値するわけではありません。どれが該当するかは ContentTypes リストが決めており、妥当な既定値が用意されています。text/*application/jsonapplication/javascriptapplication/xmlapplication/xhtml+xml、そして image/svg+xml です。/* で終わるエントリはファミリー全体にマッチするので、text/* は HTML、CSS、プレーンテキストなどをまとめてカバーします。

テキスト、HTML、CSS、JavaScript、JSON は、繰り返されるトークンやタグ名、空白に満ちているため非常によく圧縮されます。すでに圧縮済みの形式はそうではありません。JPEG、PNG、WebP 画像、ZIP アーカイブ、.gz ファイル、MP4 動画はすでに冗長性が絞り出されており、zlib にもう一度通しても CPU を消費するだけで、ほぼ同じサイズ、ときにはわずかに大きなボディができあがります。これらのコンテンツタイプはリストに含まれていないので、スキップされます。

リストは独自のものに置き換えられます。

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;

この判定は charset サフィックスを無視するので、Content-Type: text/html; charset=utf-8 のレスポンスも text/* にマッチします。サーバーにはもうひとつ念のための規則があります。圧縮後のボディが元のサイズより小さくならなかった場合、圧縮は破棄され、プレーンなボディが送られます。圧縮しなかった場合よりも多くのバイトを送ってしまうことはありません。

圧縮レベルと最小サイズ

Level は通常の zlib のレベルで、1 から 9 まで、既定は 6 です。この既定値は速度と圧縮率の標準的な妥協点であり、世界中のほぼすべての Web サーバーが採用している値です。9 に上げると、すべてのレスポンスで CPU コストが目に見えて増えますが、JSON ではほとんど得るものがありません。非圧縮ボディに対する大きな勝利はすでにレベル 1 で得られており、レベル 6 から 9 は最後の数パーセントを争っているにすぎないのです。サーバーが CPU バウンドなら、9 に上げるよりも 3 や 4 に下げてみるほうがはるかに興味深い実験になります。

MinSize の既定は 1024 バイトで、それより小さいボディはそのまま送られます。gzip ストリームは自身のヘッダーとトレーラーを持っており、1 キロバイト程度を下回ると、フレーミングのオーバーヘッドと CPU コストが、節約できるわずかなバイト数に見合いません。小さな JSON の確認応答や短いリダイレクトはこのカテゴリに入りますし、そうあるべきです。

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;

何が得られるのか

効果はコードではなくネットワーク上に現れます。HTML ページや JSON API のレスポンスは反復性が高いので、数十キロバイト程度の典型的なペイロードは、サーバーを離れる前にたいてい数分の一に縮みます。ネットワーク上のバイト数が減れば、それを届けるためのラウンドトリップも減ります。そして本当に効いてくるのはそこです。モバイル回線や高レイテンシのリンクでは、レスポンスを届ける時間は帯域幅ではなくラウンドトリップの回数に支配されます。より少ないパケットに収まるボディは、単純により早く到着するのです。

コストはサーバーの CPU で、レスポンスごとに一度だけかかり、既定のレベルではわずかです。テキスト中心のサイトや JSON API では、この取引はほぼ常に受け入れる価値があります。

自分で組み立てるレスポンス

イベントハンドラーで特別なことをする必要はありません。いつもどおりにレスポンスを組み立て、ContentText または ContentStreamContentType を設定すれば、クライアントが圧縮を要求していてコンテンツタイプがリストに含まれている場合、サーバーが送信時に圧縮します。

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;

圧縮のステップはハンドラーが戻った後に実行されるので、クライアントが受け取る Content-Length は圧縮後の長さになり、Content-Encoding も自動的に設定されます。すでに圧縮済みのボディを配信するために自分で ContentEncoding を設定した場合、サーバーはそのレスポンスにまったく手を加えません。

2 種類のレスポンスは意図的に決して圧縮されません。バッファリングされてはならない Server-Sent Events ストリーム (text/event-stream) と、204 や 304 のようなボディを持たないレスポンスです。

DocumentRoot から配信されるファイル

静的ファイルもコードをまったく書かずに同じ扱いを受けます。サーバーにフォルダを指定し、圧縮を有効にすれば、その下の HTML、CSS、JavaScript、SVG、JSON ファイルは送信時に圧縮されます。同じフォルダにある画像やアーカイブは、コンテンツタイプがリストに含まれていないので圧縮されません。

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

HTTP.sys サーバー

Windows の HTTP.sys サーバーである TsgcWebSocketServer_HTTPAPI も、同じプロパティと同じ既定値を持つ同一の HTTPCompression オプションオブジェクトを備えています。ひとつだけ正確に述べておくべき違いがあります。HTTP.sys サーバーではこのプロパティが published ではなく public なので、オブジェクトインスペクタには表示されません。サーバーをアクティブにする前に、コードで設定する必要があります。

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;

今のところ HTTP/1.1 のみ

このリリースでは、圧縮されるのは HTTP/1.1 のレスポンスだけです。HTTP/2 と HTTP/3 のレスポンスには手が加えられません。サイトを HTTP/2 で配信している場合、この機能はまだ何も変えません。プロパティを有効にしたのになぜ何も縮まないのかと悩ませるよりも、この点をはっきりさせておく価値があります。

提供状況

HTTP レスポンス圧縮は sgcWebSockets 2026.7 で、Delphi 7 から 13 および C++Builder 向けに、Win32、Win64、Linux64、macOS で利用できます。既定では無効です。HTTPCompression.Enabled := True を設定するまで、アップグレードによって既存のサーバーが変わることはありません。

有効なサブスクリプションをお持ちのお客様は、カスタマーエリアから新しいビルドをダウンロードできます。トライアルをご利用の方は esegece.com/products/websockets/download から更新版のインストーラーを入手できます。

ご質問、ご意見、あるいはご自身のサーバー向けにコンテンツタイプを調整するお手伝いが必要ですか。お問い合わせください。コードを書いた本人から返信が届きます。