QUICの信頼性のないデータグラム拡張(MESSAGEフレーム/Datagramフレーム)

QUICはUDP上で暗号化された信頼性のあるデータ通信を提供するトランスポートプロトコルです。

現在IETFの「QUIC WG」で標準化が進んでおり、先行して実装されていたGoogle版QUICもIETF版QUICへの移行が進められています。

QUICのアプリケーションレイヤ

現在QUIC上のアプリケーションプロトコルとしては「HTTP over QUIC」にフォーカスして標準化が進められています。

一方で、WebRTC over QUIC (Quartc)、QUIC as a VPN (QBone)、DNS over QUICといったその他のアプリケーションプロトコルでもQUICを使用する考えを持ってる人もいます。

そこでそういったプロトコルのトランスポートとして使用できるように、QUIC上でロスしたパケットに含まれるデータを再送しないデータ通信を可能にする拡張仕様が提案されています。もちろん通信は暗号化されていますし、Ackにより相手が受信したかどうかは確認することが出います。

MESSAGEフレーム/Datagramフレーム

提案自体は同時期に個別に2つの仕様が出ています。

1つ目の「QUIC Messages」はGoogleのIan Swett氏提案しているMESSAGEフレームを定義し利用するもので、Google QUIC v45ですでに実装されています。

2つ目の「An Unreliable Datagram Extension to QUIC」はApple人らによって提案されているDATAGRAMフレームを定義利用するものです。著者はIETF102において、Ian Swett氏との議論に基づいてDraftを書いたと述べています。

これらはフレーム名を除いて同じもののようです。実際に、Ian Swett氏は共同で作業していく意思を示しています。

DATAGRAMフレーム

DATAGRAMフレームのフレームタイプは、0x1c又は0x1dであり最下位ビットが1の場合はLengthフィールドを持ちます。0の場合はパケットの最後まででデータであることを意味します。

DATAGRAMフレームのフォーマットは次のとおりです
f:id:ASnoKaze:20180922223804p:plain

STREAMフレームの場合はストリームIDによって一つのコネクション上に多重化されますが、DATAGRAMフレームの場合は多重化するのならQUICを利用するアプリケーションの責任によって実施されます。

DATAGRAMフレームのみのパケットにはAckする必要がありますが、ロス回復には使用されないためAckを遅延させてバッチ的に応答すべきです(SHOULD)。DATAGRAMフレームはコネクションレベルのフロー制御を受けます。また、輻輳制御やフロー制御によりブロックされた場合はそのままドロップすることもできます(MAY)

ブラウザからシリアルポートにアクセスするSerial API

blink-devメーリングリストに「Intent to Implement: Serial API」として「Serial API」の実装に着手する旨の投稿がされている。

この「Serial API」はW3CのWICGで議論がされており、ブラウザからシリアルポートにアクセス可能にする。3DプリンタやArdbinoなど、様々なデバイスと接続できるようになる。

パーミッションの要求については、すでに実装されている「Web Bluetooth」「WebUSB API」とおなじになるようだ。

sample

仕様では、Ardinoからデータを読み込む例が示されている

//Request the list of ports from the user
SerialPort.requestPorts().then(ports => {

  //Pick the first matching port
  var serial,
      kind = "Arduino",
      key = "manufacturer",
      //find the Arduinos!
      arduinos = ports.filter(port => port.get(key).search(kind) > -1);

  if (arduinos.length) {
     serial = new SerialPort(arduinos[0].path);
     serial.in.read(readData)
  }

  function readData(){
    while(let data = yield serial.read()) {
      console.log(data);
    }
  }
})
.catch(console.error);

Chrome 71で検討されている iframeへのPermission Delegation

Webサイトがカメラや位置情報をアクセスする際にパーミッションを要求する場合がある。iframeで埋め込んだページでも同じようにパーミッション要求が行える。

Chrome 71で導入が検討されている「Permission Delegation」では、iframeで埋め込まれたページでのパーミッション要求の仕組みがわかりやすくなる。(iframe内のドメインからパーミッション要求されても、普通のユーザにはわかりにくい)

iframe内でパーミッションが必要になった際、top level origin(URLバーに表示されているサイト)がパーミッションを要求し、その権限を移譲する形になる。

その結果各パーミッションはtop level originに紐づくため、ユーザは権限の管理が行いやすくなる。

https://asnokaze.com/pd.html は以下のように、iframeを埋め込む。allow属性でiframe内で許可するパーミッションを指定する

<iframe allow="geolocation;camera;" src="https://permission.site">
iframe内でカメラへのアクセスを要求する

  • Chrome 69では、iframe内のドメイン名(permission.site)が要求していると表示される
  • Chrome 71では、のtop level origin(asnokaze.com)が要求していると表示される
パーミッションの削除

Chrome 71では、権限はtop level originに紐付いているので、権限の削除もtop level originから削除すれば良い

HTML要素からHTTP/2優先度を指定する Priority Hints が動いた

HTTP/2にはクライアントからHTTPリクエストの優先度を指定することができる。サーバはその優先度に基づいてHTTPレスポンスを返す(無視しても良い)。
一般的にはブラウザ自身が判断し、ページのレンダリングを早くするためにCSSなどは優先度を高く、画像は優先度を低く設定している。

一方で、Webサイトを作る側としては、最初に表示される領域(Above the fold)の画像を優先度高くしてほしいと行ったこともあるだろう。

そこで、HTMLからリクエストの優先度を指示できる「Priority Hints」という仕様の標準化が進められている。

すでに、Chrome Canary (71)で動作するようなので試す。

importanceに"high"や"low"を指定する

     <img src="img/1.png" importance="high">
     <img src="img/2.png" importance="low">
     <img src="img/3.png" importance="low">
     <img src="img/4.png" importance="high">

デベロッパーツールで確認すると
f:id:ASnoKaze:20180912010303p:plain

Priorityの欄が指定したとおりになってる事が確認できる。

仕様

importanceには以下が指定できる

  • high
  • low
  • auto

その他

動作確認はしていないが、その他にもいくつかの使用例が書かれている

preloadでも

HTMLから

<link rel="preload" as="script" href="critical-script.js">
<link rel="preload" as="style" href="theme.css" importance="low" onload="this.rel=stylesheet">

レスポンスヘッダから

Link: </app/style.css>; importance=high
Link: </app/script.js>; importance=low
fetchでも
<script>
 fetch('/api/articles.json', { importance: 'high' }).then(/*...*/)
 fetch('/api/related.json', { importance: 'low' }).then(/*...*/)
</script>

キャッシュがHitした情報を示すCacheヘッダの標準化提案

CDNなどのサービスは、リクエストがキャッシュにヒットしたかどうかをx-cacheヘッダに格納してレスポンスしてくれます

このx-cacheヘッダは独自の形式であり、例えば以下のようになっています。
CDNによっては、x-cache以外のヘッダを使うものもあります)

fastly (document)

X-Cache: MISS, HIT

Amazon CloudFront

X-Cache: Hit from cloudfront

先述のように、x-cacheヘッダは標準化されておらずCDN毎に独自に使用されています。

そこで、IETFのHTTbisにおいて
キャッシュのHit情報などを格納するcacheヘッダの標準化を行う「The Cache HTTP Response Header」という仕様がFastlyのMark Nottingham氏から出されています。

Cacheヘッダ

Cache: HIT_FRESH; node="reverse-proxy.example.com:80";
                    key="https://example.com/foo|Accept-Encoding:gzip",
           HIT_STALE; node="FooCDN parent"; fresh=-45; age=200; latency=3,
           MISS; node="FooCDN edge"; fresh=-45; age=200; latency=98

(シンタックスについては 以前書いた「Structured Headersの記事」を参考に)

リクエストが複数回forwardされた場合は、レスポンスを返す順にカンマ区切りで情報が追記されていく。頭のほうがオリジンに近いほうで、後ろのほうがユーザエージェントに近い。

cache-actions

cache-actionsと呼ばれる、リクエストに対してどういうアクションを取ったかは以下の種類がある

  • HIT_FRESH: リクエストをforwardすることなく、キャッシュとして期限内(fresh)であるものを返した
  • HIT_STALE: リクエストをforwardすることなく、キャッシュとして期限内(fresh)ではないものを返した
  • HIT_REFRESH_MODIFIED: キャッシュが有効ではなかったので、リクエストをforwardして新しく得られたレスポンスを返した
  • HIT_REFRESH_NOT_MODIFIED: キャッシュが有効ではなかったので、リクエストをforwardして有効性が確認できたキャッシュしていたレスポンスを返した
  • HIT_REFRESH_STALE: キャッシュが有効ではなかったので、リクエストをforwardしたが問題が発生したため、保存してあったキャッシュを返した
  • MISS: キャッシュが無かったためリクエストをforwardした
  • MISS_CLIENT: リクエスにCache-Controlヘッダのような、キャッシュが使えないようなパラメータが含まれていたので、リクエストをforwardした
  • BYPASS: レスポンスをキャッシュするように設定されていない
  • ERROR: 保存してあったものか、forwardして得られたキャッシュが使用できなかった
パラメータ

各アクションは下記パラメータを付けることが出来る(OPTIONAL)

  • node: ホスト名やIPアドレスといった、キャッシュノードの識別子
  • fresh: freshさの有効期限([https://tools.ietf.org/html/rfc7234#section-4.2.1:title=[RFC7234], Section 4.2.1])。秒で示され、負の値を取ることも出来る。
  • age: このキャッシュの age を示す([https://tools.ietf.org/html/rfc7234#section-4.2.1:title=[RFC7234], Section 4.2.3])
  • cacheable: このレスポンスをキャッシュとして保存できるかのboolean値
  • key: キャッシュがレスポンスと関連付けられるキー
  • latency: リクエストを受け取ってからレスポンスを返すまでの遅延ミリ秒
  • cl_nm: クライアントへの応答に304 Not Modifiedが含まれるかを示す

DNS over HTTPSサーバを見つけるためのTXTレコードの提案仕様

DNS over HTTPS(DoH)の標準化が進められており、引き続き注目を集めている。
asnokaze.hatenablog.com

一方で、DoHサーバをどのように見つけるか、見に行かせるかの議論が始まっています。
JPNIC Blog :: DNS over HTTPSとDHCP -IETF102における議論- で紹介されている通り、先月行われたIETF102でもDNSゾルバの識別子と利用についてのBoFがありました。

例えば、設定ファイルだったり、DHCPDNSサーバを設定する際にDoHサーバを設定する際にどうすればいいか、どのようディスカバリさせるかといった議論です。

その流れで、DoH関連の標準化を推し進めているPaul Hoffman氏より「Associating a DoH Server with a Resolver」という提案仕様が出ています。

この手提案仕様では、主なユースケースとして名前解決をDoHに切り替えたいブラウザとしており、その次としてOSとしています。

Associating a DoH Server with a Resolver

この仕様では、resolver-associated-doh.arpa に対してTXTレコードを引くことで、そのリゾルバと関連付けられているDoHサーバのURIを取得できるようにします。

この仕組を解釈するリゾルバは、問い合わせに対してもし関連付けられているDoHサーバがあればそのURI (例 "https://dnsserver.example.net/dns-query{?dns}" )を返し、なければ0x00を返します。

おそらくこんな感じ

;; ANSWER SECTION:
resolver-associated-doh.arpa.  600  IN  TXT  "https://example.com/dns-query{?dns}"

DoHサーバを利用する場合は、レスポンスのTTL時間の間だけ使用されることが推奨されています(SHOULD)。

resolver-associated-doh.arpaは「Special-Use Domain Names」として予約されることになります。

Loading Signed Exchangesの仕様 (WebPackaging)

WebPackagingと呼ばれる仕組みが議論されているのは、以前このブログでも紹介したとおりである。
asnokaze.hatenablog.com

この仕組みは3つの仕様からなる。

署名されたHTTP Exchangesをユーザエージェントがどのように読み込むかを定義したLoadingの仕様ですが、
、他のものにくらべ少々遅くなったが、「Loading Signed Exchanges」としてリポジトリに追加されていたので簡単に目を通す。

が、Fetch力が高くないので詳しい人に補足頂ければ幸いだ。

Loading Signed Exchanges

僕なりの理解としては、大まかなLodingの流れは以下の通り。

Signed HTTP Exchangesの一形式であるsxgファイルを https://distributor.example.org/foo.sxg から読み込んで、署名されているhttps://publisher.example.org/foo からのHTTPレスポンスを読み込む例を示す。

f:id:ASnoKaze:20180824015818p:plain

  • ユーザエージェントがhttps://distributor.example.org/foo.sxgをフェッチする
  • 取得したfoo.sxgのMIME Typeがapplication/signed-exchangeであることを確認し、signed exchangesをパースし、証明書や署名や有効期限などもろもろの確認を行う。
  • もともとのリクエストを https://publisher.example.org/foo にリダイレクトする (通常通りのリダイレクト)
  • そのリダイレクトが signed exchanges に含まれるリクエストとマッチすることを確認
  • signed exchangesからレスポンスを得る

具体的には、Fetchの使用に対してMonkey Patchを当てるとともに、公開鍵や署名といったValidationのアルゴリズムを定義している。またService Workerでの扱いについても言及している。