HTTPヘッダに構造定義を与える Structured Headers の提案仕様 (draft-14)

2年前にも同様の記事を書きましたが、当時はdraft-00でしたが、2年ほど立ち draft-14が出ておりところどころ変わっているので書き直します。
asnokaze.hatenablog.com

背景

新しいHTTPヘッダを定義するときに、新しい仕様毎にシンタックスを記述するのは非常に煩わしい作業です。

そこで、リストや辞書形式のようなデータ構造の定義を「Structured Headers for HTTP」として与えることで、新しいHTTPヘッダを定義するときはこの仕様を参照しリストや辞書形式のヘッダを定義できるようになります。実際、近年提案されている仕様ではしばしば参照されています。

また仕様ではこれらのデータ構造のパース手順も定義されており、実装としてもパースの処理が汎用的に再利用できるようになります。

// 2020/03/10追記
(最新仕様では 名称が "Structured Field Values for HTTP" に変更されました)

概要

Structured Headersでは、

  • トップレベルとして「Lists, Dictionaries, Items」という3つの形式がある
  • ListsとDictionariesはコンテナであり、Items もしくは Inner Listsを持つ
  • ItemsとInner ListsはKey/Value形式でパラメータ化できます
Lists

ListsはItemやInner Listsを持ち、カンマで区切られます。

Example-StrListHeader: "foo", "bar", "It was the best of times."
Inner Lists

Inner Listsはカッコで囲まれ、itemをスペース区切りで持ちます

Example-StrListListHeader: ("foo" "bar"), ("baz"), ("bat" "one"), ()
Parameters

ParametersはitemやInner Listsに関連付けられたKey/Valueのペアです。itemやInner Listsの後ろに;で記述されます。Keyは文字で、valueはitemとなります

Example-ParamListHeader: abc;a=1;b=2; cde_456, (ghi;jk=4 l);q="9";r=w
Dictionaries

DictionariesはKey/Valueからなる順序付きのマップです。

   Example-DictHeader: en="Applepie", da=*w4ZibGV0w6ZydGU=*
   Example-DictListHeader: rating=1.5, feelings=(joy sadness)
   Example-MixDict: a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid=?1
Item

Itemは下記の種類があります

  • Integers
  • Floats
  • Tokens
  • Byte Sequences
  • Booleans

それぞれ例を示します

Integers

Example-IntegerHeader: 42 

Floats

Example-FloatHeader: 4.5

Strings (ダブルクォートで囲まれます)

Example-StringHeader: "hello world"

Tokens (短い単語)
(アルファベット及び":" "/"を持つ。なぜかこれだけ仕様で例が示されていない)

Byte Sequences (*で囲まれたBase64)

Example-BinaryHdr: *cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==*

Booleans (?で始まり、0 or 1)

Example-BoolHdr: ?1

使用例

このStructured Headersは多くの仕様で参照されていますが、一つの例をあげます。

FastlyのMark Nottingham氏が提案する「The Cache-Status HTTP Response Header」では、CacheにHitしたかなどをレスポンスヘッダで通知する新しいヘッダを定義しています。

このようにCache-StatusはListであると定義しています。このようにシンタックスの定義はStructured Headersを参照するだけになります。

   The Cache-Status HTTP response header indicates caches' handling of
   the request corresponding to the response it occurs within.

   Its value is a List [I-D.ietf-httpbis-header-structure]:

Listの中身についてはもう少し細かい言及があるのですが、詳細は割愛します
実際のCache-Statusヘッダの例です。

Cache-Status: "CDN Company Here"; res-fresh=545,
                 OriginCache; cache-fresh=1100; collapse-hit=?1

Parameters及びBooleansが使用されている事がわかりますね。

その他

このようなHTTPヘッダでデータ構造を扱うという案が出たとき、最初はJSONを使うことが検討されましたが、議論のすえ現在の形式となっています。その経緯については提案仕様の Appendix-B に書かれています。興味ある方はよんでみてはいかがでしょうか。

HTTPで部分的アップロードを可能にする Partial Uploadsという提案仕様

HTTPリクエストは一度中断してしまうと、途中から再開することは出来ません。特に大きなファイルをアップロードしている場合は、最初からやり直すことになってしまいます。(ダウンロード自体は、Content-Rangeを指定することで部分的な取得が可能です)

そこで、細かく分割して部分的なアップロードを可能にする「Partial Uploads in HTTP」という仕様が提出されています。

この仕様では、PATCHメソッドとContent-Rangeヘッダを使用して部分的なアップロードを行います。また、部分的なアップロードに対するレスポンスコード2xx Sparse Resource(具体的な番号はTBD)を新しく定義しています。

部分的なアップロードは、Content-Typeにmessage/byterangeを指定し、PATCHメソッドでデータを送信します。message/byterangeはmultipart/byterangesのような構造を持ちますが単一のRange領域からなります(Content-Rangeは必須)。

部分アップロードの最初の1リクエストは以下のようになります。この例では600バイト中200バイトをアップロードしています。

   PATCH /uploads/foo HTTP/1.1
   Content-Type: message/byterange
   Content-Length: 281
   If-None-Match: *

   Content-Range: bytes 0-199/600
   Content-Type: text/plain
   Content-Length: 200

   [200 bytes...]


つづけて、次の200バイトをアップロードする場合は、以下のようになります。最初同様message/byterangeを指定します。

   PATCH /uploads/foo HTTP/1.1
   Content-Type: message/byterange
   Content-Length: 283
   If-None-Match: *

   Content-Range: bytes 200-399/600
   Content-Type: text/plain
   Content-Length: 200

   [200 bytes...]

部分アップロードを行うリクエストに対するレスポンスのステータスコードは2xx Sparse Resourceを使用します。このSparse Resourceレスポンスは、データ全体を受信したわけではないが該当の部分アップロードリクエストはエラーではないことを示します。

すべてのデータ全体がアップロードされた際のステータスコードは200 OKを返します。

ネットワークメトリクスを示すTransport-Info HTTPレスポンスヘッダ

トランスポートレイヤの性能を示すTransport-Infoレスポンスヘッダの提案仕様「The Transport-Info Response Header」が提出されています。

この仕様の目的は、ブラウザでより詳しいトランスポートレイヤのパフォーマンスを取得することが目的となっており。HTTPレスポンスヘッダに通信の情報(RTTやMSS)が格納されます。

W3Cで議論されている「Network Information API」よりもより詳しい情報が取得できます。

Transport-Infoレスポンスヘッダ

以下のように、Structured Headersの規則に則って表現されます。

Transport-Info = ExampleEdge; ts=1567176968.69; alpn="h2"; cwnd=24;
                       rtt=250; mss=1460; rttvar=10; dstport=12345

まず最初に製品やサービスを示す識別子(例ではExampleEdge)が入ります。エッジサーバ側のメトリックが続きます。意味は下記のとおりです

  • tls: unitタイムのタイムスタンプ
  • alpn: ALPN識別子
  • cc_algo: 輻輳制御アルゴリズム
  • cwnd: 輻輳ウインドウ
  • rcv_space: 受信ウィンドウ
  • dstport: 送信先ポート
  • mss: Maximum Segment Size
  • rtt: ラウンドトリップタイム (ミリ秒)
  • rttvar: RTTVAR (RFC6298)
  • send_rate: 一秒あたりの送信データ量 (キロバイト/秒)

DoHを利用しないように指示するCanary domainの仕組み

DoHはプライバシー向上のための重要な技術です。

一方で、DoHのディスカバリ、アプリケーションに対してのConfiguration方法、ポリシーの適応など、いくつかの課題が議論されています (IETFの資料を参照)。

課題の一つに、DoHの利用が適さないネットワークにおいて、それをどうブラウザ(クライアント)に通知するのかの問題がある。

DoHの利用が適さないネットワーク/ユーザとは

  • ペアレンタルコントロールなど、子供に不適切なサイトへのアクセスを抑制する必要がある場合
  • 会社内など、社内用途でのドメインを利用している場合

現在このようなネットワークでDoH使用を制御する方法は標準化されていませんが、FirefoxではCanary domainという仕組みを用いてこれを実現しています。

Canary domain

Fifrefoxでは、そのネットワークでDoHを使用するか判断するにあたって「use-application-dns.net.」の名前解決を利用します。

実際にアクセスするとMozillaの解説サイトに飛びます。もちろん名前解決もできます。

$ dig @8.8.8.8 use-application-dns.net. +short
63.245.208.212

Firefoxは use-application-dns.netの名前解決をしてDoHを使用するか決めます。
OSに設定されたDNSサーバを利用して、use-application-dns.net. の名前解決し、以下のどちらかの場合はDoHを使用しません。

  • NXDOMAIN(存在しないドメイン)やSERVFAILなど、NOERROR以外のレスポンスコードが返ってきた
  • NOERRORレスポンスコードだが、AやAAAAレコードが含まれない

ネットワーク管理者(社内DNSISPDNS管理者)がuse-application-dns.netの名前解決に対してNXDOMAINを返すようにすることで、ユーザに対してDoHの利用を抑制できるようになります(DoHが無効になる条件は他にもあり、OSのペアレントコントロール設定なども検出します)。

利用状況

NXDOMAINを返す、ISP内のリゾルバの割合です。
f:id:ASnoKaze:20191201160941p:plain
https://www.isi.edu/~hardaker/news/20191120-canary-domain-measuring.html

その他

悪用されない方法で、適切にポリシーを適応する方法は悩ましいなあと思う今日このごろ

QUIC, HTTP/3の標準化状況を確認したい (2019年11月版)

2020/06/01追記
まるっと解説記事を書き直しました
asnokaze.hatenablog.com


目次

QUICは現在IETFで標準化が進められているトランスポートプロトコルであり、HTTP/3はそのQUICの上で効率よくHTTPのメッセージをやりとりするプロトコルです。

ChromeFirefox, Nginxなどがすでに実装を行っており、「相互接続テスト」を定期的に実施している。その他多くの実装があり、「Implementations」から確認ができる。

その標準化状況について、QUIC WGとHTTP WG両方のチェアを務めるMark Nottinghamが先週行われたIETFで発表した「Quick QUIC Update」の資料が良かったので、大変おこがましいが自分なりに補足しながら紹介したい。

技術的な詳細は過去に紹介しているので、"QUICカテゴリ"をご覧いただければと思います (関連記事をブログ最後に羅列しておきます)。

Status

QUIC WGでは、下記の4つをコアドキュメントとして扱っている。現在 draft-24であり、各ドキュメントとも歩調を合わせて改定されている。(その他にもQPACKやオペレーションに関するドキュメントも扱っている)

QUIC WGでは標準化にあたってEarly-Stage ProcessとLate-Stage Processという2段階のステージにのっとって議論を進めている。Early-Stage Processではドキュメントの著者の裁量が強く早く柔軟性が強いです。Late-Stage Processでは、WG内でのコンセンサスが得られたものが仕様に反映されます。(プロセスの詳細はURLを参照

現在はTransport, TLS, Invariants(説明は割愛)がLate-Stage Processです。HTTP/3, QPACK, RecoveryはEarly-Stage Processですが、Late-Stage Processへの移行も近そうです。

また、先述の通り実装をもちよっての相互接続テストを実施し仕様の確度を高めています。

*2019/11/27追記
HTTP/3, QPACK, RecoveryはLate-Stage Processになりました
https://mailarchive.ietf.org/arch/msg/quic/hxadXDI53V83dCiNe-bKGaws8-A

The Plan

2019年末頃にWorking Group Last Callが予定されています。実装及びデプロイ結果からのフィードバックを受け付けるため、長い停止時間が設けられます。

2020年中頃に、IETFの技術的責任を持つIESG(Internet Engineering Steering Group)に送られレビューされます。

その後RFCとしての体裁が整えられ、RFCとして出版されることになります。

Versions

QUICではたくさんの仕組みや機能が導入されていますが、闇雲になんでも機能追加するわけではなくQUICv1として世の中に出す機能は絞られてきました。そのため、FECやマルチパスといったいくつかの機能はQUICv2へと見送りになってきました。

QUICv2が問題なく使えるように、バージョンのネゴシエーション・管理の仕組みや、ossification(硬直化)を避ける仕組みが議論され導入されています。

しかし、現時点でQUICv2がどの様になるかは、パケットファーマット(invariants以外の部分)や機能含め具体的な議論は始まっていません。

Extensions

QUICは拡張できるようになっています。

現在検討されているもの

  • QUIC Load Balancers (duke-quic-load-balancers)
  • QUIC Version Negotiation (schinazi-quic-version-negotiation)
  • QUIC Datagrams (pauly-quic-datagram)

QUICのロードバランサとバックエンドが連携するための仕様、バージョンをより柔軟にネゴシエーションする仕様、再送を必要としないアプリケーションデータの送信などが拡張として議論されています。

その他に、下記の仕様が検討されています。

  • Loss Bits (ferrieuxhamchaoui-tsvwg-lossbits)
  • 0RTT-BDP (kuhn-quic-0rtt-bdp)
  • Multipath (deconinck-quic-multipath)

経路上からQUICコネクションでパケロス発生したことを観測できるようにする仕様、経路特性を学習し接続を再開したときによりスループットを良くする仕様、Wi-Fiとキャリア回線など複数経路を用いて通信を行うマルチパスの仕様などが議論されています。

Applications

QUICはトランスポートプロトコルですので、アプリケーションプロトコルはHTTP/3に限りません。

多くのアプリケーションプロトコルがQUICの利用を検討しています

  • WebTransport (vvv-webtransport-quic)
  • Media
  • Proxy/tunnelling (e.g., draft-schinazi-masque)
  • Others (e.g., DNS, NETCONF)

WebSocketのようなWebで双方向通信を行うWebTransport(この上でメディアも流すユースケースもあります)や、RTP over QUIC、VPNのようにつかうmasqueプロトコルDNS over QUICなど様々です。

Other Things

その他にも、シミュレータツール、デバック用ログフォーマット、衛星通信での利用などのトピックがあります

  • QUIC Network Simulator
  • QLog logging format (marx-qlog-main-schema)
  • Pluginised QUIC
  • QUIC for SATCOM (kuhn-quic-4-sat)

More Information

その他の関連事項については、下記を参照
https://github.com/quicwg/base-drafts/wiki/Related-Activities

  • 拡張仕様は、ドラフトを書いてQUIC WGへ
  • アプリケーションは、ドラフトを書いてArea Directorへ

関連記事

手前味噌だが、今回取り上げたトピックの多くを本ブログで紹介済みである

仕様関連 (日付降順)

実装関連 (日付降順)

Qiita

ブラウザでIPマルチキャストを受信するMulticastReceiver API

目次

WICGやIETFにおいて、ブラウザでIPマルチキャストのパケットを受信できるようにする「MulticastReceiver API」の議論があるようです。

Akamaiの人らによって提案が行われています。その中で「人気コンテンツの大規模配信に対応していく」ことがモチベーションとしてあげられています。

この提案では、いかの事をゴールとしています

  • 1対多のマルチキャストをsubscribeする方法を提供する (送信はスコープ外)
  • 多くのユースケースで使用できるAPIを提供する。
  • トラフィック(パケット)を暗号的に認証する (改変されてないことがわかるようにする)
  • ネットワークの安全性を確保する。大量のトラフィックからネットワークを守る

IPマルチキャスト

ユニキャスト通信では、同じデータがクライアントの数だけルータ上を通ることになります。
f:id:ASnoKaze:20191124200553p:plain

マルチキャストでは、ルータ上でコピーされ必要なクライアント(リスナー)に届けられます。
f:id:ASnoKaze:20191124200635p:plain

各リスナーはSource と Groupを指定してsubscribeします。

MulticastReceiver API

MulticastReceiver Explainer」に例があります。
JavaScriptからsource、group、portを指定してsubscribeします。メタデータを提供するdormsサーバ(後述)を指定します。うけとったパケットはprocessPacketsで処理を行えます。

var mrc = new MulticastReceiverConfig();
mrc.source = '198.51.100.10';
mrc.group = '232.10.10.1';
mrc.port = 5001;
mrc.dorms = 'dorms.example.com';

var mr = new MulticastReceiver(mrc);
// process any packets received
mr.onmessage = function(evt) { processPackets(evt.data); }

// monitor other events, but do nothing with them.
mr.onjoin = function(evt) { console.log('multicast receiver joined'); }
mr.onleave = function(evt) { console.log('multicast receiver left'); }
(略)

mr.join();

プロトコル

Multicast to the Browserを実現するためのプロトコルIETFのMulticast Backbone Deployment (mboned) WGで議論されています。mboned WGは、グローバルインターネット、ドメイン間でマルチキャストルーティングプロトコルと、そのデプロイ、運用について議論をしているWGになります。

現在、ブラウザへのIPマルチキャストに関して、AkamaiのJake Holland氏らによって下記の3つの文書が提案されています。 これら3つを組み合わせてブラウザへの配信を実現させます。

DORMSはsource-specific multicast (SSM)に関する拡張可能なメタデータをディスカバリ・取得する方法を定義します。
AMBIはブラウザが受信したデータの整合性を検証し、パケットロスを監視する方法を定義します。
CBACCはブラウザがネットワークキャパシティ上限を超えないようにする方法を定義します。

ブラウザがゲートキーパーになる場合は図のようになります (経路上の装置がゲートキーパーになる場合もある(資料参照))
f:id:ASnoKaze:20191124204622p:plain
(IETF106 Mbonedの資料より(URL))

雑感

資料ではマルチキャストQUICにも触れているが、上位プロトコルに関しては今の所具体的なところは書かれていない (実験できるようにするのが大事とのこと)。
asnokaze.hatenablog.com

IPマルチキャスト難しい。

HTTP/3と新しいプライオリティ制御方式について

目次

関連記事

HTTP/3については以前書いたとおりです
asnokaze.hatenablog.com

追記 (2019/11/21)

https://tools.ietf.org/html/draft-kazuho-httpbis-priority-04」draft-04で、以下のようにurgencyとincremental頭文字で表現されるようになりました

   priority = u=4, i=?1

SETTINGSパラメータもSETTINGS_DEPRECATE_HTTP2_PRIORITIESを使うように変更されました

背景

HTTP/2から複数のHTTPリクエストが一つのコネクション上で並列的に送信されるようになりました。それにともない、クライアント側からリクエストの優先度(プライオリティ)をサーバに通知する仕組みが導入されました。

Webページをレンダリングを開始するのに必要なリソース(HTML, CSSなど)の優先度を高く設定したりできます。

現在標準化が進められているHTTP/3でも、最初はHTTP/2と同様、リクエストの依存関係(dependency)と重さ(weight)で優先度を管理する方式(ツリー方式)が検討されていました。

しかし、議論が進むに連れこのツリー方式の優先度制御は複雑であり、あまり実装されていないことがわかりました。(Interimの資料)

HTTP/3では新しい優先度制御方式も議論されましたが、一旦HTTP/3の仕様からは優先度制御の仕組みは外され、優先度制御はオプショナルな機能とする方向となりました。

実際、HTTP/3の仕様からはすでに優先度制御の機能は削除されています。
github.com

またHTTP/2でも優先度制御を行わないで、拡張仕様の優先度制御方式をネゴシエーションする仕組みを検討することになりました。

優先度制御の仕様は、別途デザインチームを結成しそこで議論されることになりました。

ここらへんの議論は、kazuhoさんの記事に書かれているとおりです。
blog.kazuhooku.com

プライオリティの仕様の方向性

HTTP WGのメーリングリストに投げられている通り(URL)、先日行われたQUIC WGの中間会議においてデザインチームのリーダであるIan Sweet氏より、Fastlyのkazuhoさんが提案している「Extensible Prioritization Scheme for HTTP」をベースとして進めていくことがアナウンスされています。

Extensible Prioritization Scheme for HTTP

この仕様では、プロトコルバージョンに依存しない、リクエストここ別に絶対値で示される優先度をもつエンドツーエンドの優先度制御方式を定義しています。

具体的には以下のものが定義されています

  • Priorityヘッダ(リクエスト・レスポンスで使用される)
  • 優先度。urgency (優先度の絶対値)、プログレッシブ
  • ネゴシエーションの仕組み
  • 優先度を更新するフレーム(HTTP/2, HTTP/3の拡張フレーム)
Priorityヘッダ

priorityは下記のような形式です。フォーマットは「Structured Headers」で定義されたDictionary形式です。

   priority = urgency=3, progressive=?1

このヘッダを用いてクライアントはこのHTTPリクエストの優先度をサーバに通知します。

一方で、サーバはレスポンスヘッダにpriorityを指定することで、そのレスポンスがどれくらいの優先度で返されていることを示せます。これによって特に、そのHTTPリクエスト・レスポンスを扱っているProxyに対して、優先度を知らせることができます。

urgency と progressive

priorityヘッダは、urgencyとprogressiveというパラメータを持ちます。

urgencyは-1から6までの値をとります。値が小さいほど優先度が高いです。サーバは優先度が高いものから送信していきます。

  • prerequisite (-1)
  • default (0)
  • supplementary (1~5)
  • background (6)

それぞれ意味次のとおりです。

prerequisite: urgencyがdefaultやprerequisiteのレスポンスを止めます。ブラウザのレンダリングをブロックするようなCSSのリクエストなどに使用されます。

default: 他のリクエストをブロックしません。例えば新しいページに遷移したときにしようされます。

supplementary: この複数要求されたリソースの名から、必須ではないりそーすにつけられます。1~5の値によって優先度がしめされます(クライアントはサーバが上書きして使えるように1,5は使うべきではありません)

background: 他のリクエストに影響が出ないように後回しにできるリクエストに使用される。

ネゴシエーションの仕組み

HTTP/2およびHTTP/2で使用する優先度制御方式をネゴシエーションする方法もこの仕様で定義されています。

SETTINGS_PRIORITIESパラメータを用いてネゴシエーションします。値は次のとおりです

  • 1. H2_TREE: HTTP/2の優先度ツリー方式 (HTTP/3では使用できない)
  • 2. URGENCY: この仕様

また、urgencyとは別にprogressiveを指定できます。progressiveはそのリソースが逐次処理可能なリソースか示します(例えばprogressive jpegなど)。progressiveブーリアンであり0か1をとり、同じurgencyでprogressiveなレスポンスがあった場合は帯域を等分して使います。

優先度の更新

通信中に優先度を更新する場合もあります(例えば、ブラウザのタブを切り替えたり)。そのときに使用する拡張フレームも定義されています。

HTTP/2とHTTP/3両方にPRIORITY_UPDATEフレームを新しく定義しますが、それぞれフォーマットは異なります。HTTP/2ではストリームIDを指定します。HTTP/3ではストリームIDもしくはプッシュIDをしてします。新しい優先度はASCIIでヘッダと同じように指定します。

H2の場合
f:id:ASnoKaze:20191107015351p:plain

H3の場合
f:id:ASnoKaze:20191107015417p:plain