新しいHTTP拡張 "PRELOADフレーム" という提案仕様を書いた

こんにちはインフラの後藤です。

昨年、初めてIETFにHTTPの拡張仕様 "The PRELOAD Frame Extension"を提案したので、それについて説明してみたいと思います。

仕様の提案とは

そもそも「HTTPの拡張仕様を提案した」とはどういうことか簡単に補足します。

HTTPやTLSの標準化はIETFという組織で行われています。IETFへの仕様の提案は、基本的にはInternet-Draftを出すことによって行います。このInternet-Draftを出し、各ワーキンググループのメーリングリストで議論を行うのが一般的な流れです。

個人の書いたInternet-DraftはIndividual Draftと呼ばれ、誰でも出すことが出来ます。会員のような仕組みはなく、何かしらの審査もありません。適切なフォーマットで作成すればWeb上の提出フォームから提出するだけです。もちろん提案された仕様は誰でも見ることが出来ます。

実際の作業は、"i-d-template"を使うとMarkdownからの変換や、管理、提出まで比較的簡単に行えます。

(IPRの扱いなどはBCP 79を参照ください。)

PREALOADフレーム拡張

さて、本題です。

提出した文書 "The PRELOAD Frame Extension" で書いたとおり、HTTP/2やHTTP/3向けにPRELOADフレームを定義する提案になります。サーバがこのPRELOADフレームを使うことで、クライアントはアクセスしたWebサイトで利用されているCSSやjsなどのリソースをより早いタイミング取得できるようになります。

HTTPヘッダなどが新しく定義されることもありますが、メッセージの拡張もHTTP/2から行えるようになりました。HTTP/2ではメッセージの単位はフレームという形式になっており、フレームにHTTPヘッダやボディーデータ及び通信パラメータなどを格納して通信しています。このフレームでは独自のフレームタイプを定義出来るようになっており、"ORIGINフレーム"など幾つかの拡張仕様がすでに出ています。

HTTP/2実装は対応していない未知のフレームを単純に無視するため、既存の実装に影響なく新しいフレームを使用できるようになっています。

提案の背景

この提案の背景とモチベーションについても簡単に説明します。

TLS1.3やHTTP/3(QUIC)では、ハンドシェイク後にサーバが最初にアプリケーションデータを送信できます。

しかし、このアプリケーションデータの送信機会を有効活用できていません。サーバ側は最初に送信することになっているServer Connection Prefaceを送信するだけです。その後、クライアントからのHTTPリクエストが来てやっとHTTPレスポンス(103ステータスコードを含む)やサーバプッシュ(PUSH_PROMISE)を送信できるようになります。

この最初の送信機会を有効活用したいというのが、PRELOADフレームの目的となります。

PREALOADフレームの概要

PRELOADフレームを用いて、クライアントからのHTTPリクエストを待たずに、サーバは要求されたWebページで必要となりそうなリソースをクライアントに通知出来るようになります。より早いタイミングで必要なリソースの取得を始められるので、Webページの高速化に寄与できると考えています。

PRELOADフレームはサーバからクライアントに送信されます。下記のように"Preload"の仕様で定義されているpreload情報が含まれます。クライアント側はPRELOADフレームを受信すると、指定されたリソースのキャッシュがなければHTTPリクエストを送信しリソースを取得します。

PRELOADフレームの中身は具体的には下記に示すとおり、HTTPヘッダ圧縮の仕組みであるHPACKQPACKでエンコードされた形式で格納されます。ただし動的ヘッダテーブルは使用しません。クライアントは任意のPRELOADフレームを無視できるため、動的ヘッダテーブルを正しく同期できません。

通信フロー

TLS1.3を用いたHTTP/2でPRELOADフレームの利用イメージは下記の通りになります。

青い矢印はTLSハンドシェイクメッセージです。赤い矢印はTLS上でやりとりされるアプリケーションデータ、つまりHTTP/2のメッセージです。

TLS1.3ではサーバの方がアプリケーションデータを先に送信できます。サーバはFinishedを送信したあとに続けてSETTINGSフレームとPRELOADフレームを送信します。

そのあとに、クライアントから最初のHTTPリクエストを運ぶHEADERSフレームが送信されます。通常であれば、この最初のリクエストへの応答がないとページを表示するのに必要なリソース(css, js, img)がわからないのですが、PRELOADフレームに格納されたpreload情報のリソースも同時に取得開始できるため、それらのリソースは1RTT分早く取得できるようになります。

その他

このPRELOADフレームには幾つかの議論があると思っています。

  • PRELOADをフレーム長がSNIに依存する場合に、PRELOAD長を観測することで通信しているドメインがわかる。これは、SNIを暗号化している(ESNI)場合に問題になります
  • Linkヘッダ以外の送信について。現状は禁止しているが、個人的にはユースケースがあると思っている
    • HSTSやReport API系のヘッダはより早く送信出来たほうが好ましい(別の仕様でカバーすべき?)
    • HTTPリクエストと紐付いてないので、ヘッダの解釈上の問題が発生しうると考えている
  • 動的ヘッダテーブルの扱い

最後に

まず、この提案を出すにあたって協力していただいた社内外の皆様に感謝いたします。この記事を読んでいただいて、提案仕様に関するフィードバックも歓迎いたします。

Individual Draftは誰でも出すことが出来ます。もちろん適当でも出せばいいという話ではありませんが、皆様もアイデアがあれば是非出してみてはいかがでしょうか。