GData JavaScript のクロスドメイン通信の解析

以前の続き。JavaScriptからプライベートデータの参照、更新が出来る。Google Account Authenticationの仕組みを利用している。この前動かなかったサンプルはいつの間にか動くようになってた。

最初プロトコルは勝手にJSONPと思ってたけど、中身見てみたらIFRAME 使った fragment identifier (window.location.hashの値、URLの#以降)による通信だった。たしかに、IEで音をONにしたらクリック音カチカチする。ちなみにfragment identifierによるクロスドメイン通信は他にdojoがライブラリとして実装しているのは知っているけど、これだけ大々的にサービスで使われてるのを見たのは初めて。もっとも、ブラウザでクロスドメイン通信を達成する方法のうち現時点でもっともマシなのはこれじゃないかという意見もある

解析してみた流れを書いておく。以下、http://www.example.comにあるWebページがGoogle カレンダーのプライベートフィードを呼び出しているとする。

  • Google Account Authenticationを利用して、プライベートデータにアクセスするためのセキュリティトークンを取得する。詳細は省略。実際にはAccount AuthenticationのJavaScript APIを使えば、自動的にセッショントークンまで取得してくれる。取得したセッショントークンは勝手にcookieに格納されるため、ページをリロードしてもまた認証を求められる訳じゃない。
  • http://www.example.com/にあるJavaScriptが GData JavaScript APIでプライベートカレンダーのフィードを取得要求する (http://www.google.com/calendar/feeds/default/private/full)
  • GData JavaScript はリクエスト内容をシリアライズして、隠しIFRAMEをターゲットとしてフィードURLに対してPOSTする。このとき先にAuthenticationで取得したセキュリティトークンがリクエストパラメータとして付加される。
  • Google カレンダーはGData JavaScript からのリクエストであることを判別して、レスポンスとしてHTMLを返す。これには以下のような形でカレンダーデータがJSON形式で含まれている
<body>
<script type="text/javascript" src="http://gd.google.com/gd/api?file=gdata-xd.js&v=1"></script>
<script type="text/javascript">
xdSendResponse(
'{"version":"1.0","encoding":"UTF-8","feed":{"xmlns":"http://www.w3.org/2005/Atom","xmlns$openSearch":"http://a9.com/-/spec/opensearchrss/1.0/", ...(略)... }}',
true,
{"xdpe:dummy-uri":"http://www.example.com/dummy.gif","xdpe:request-id":"0"},
200,
'{"Last-Modified":1191589501000,"Cache-Control":"max-age=0, must-revalidate, private","Content-Type":"application/json;charset=UTF-8"}'
);
</script>
</body>
  • JSON形式のデータはIFRAME内で読み込んでいるGData JavaScriptの定義する関数(xdSendResponse)に読み込まれる
  • IFRAME内のページはhttp://www.google.comから提供されているので、そのままでは親フレーム(http://www.example.comから提供されているページ)に対してデータを受け渡すことはできない。そこでIFRAME内にさらにIFRAMEを作って、その中にhttp://www.example.com と同じURLにあるダミーイメージ(上記の場合は”xdpe:dummy-uri”に指定されている"http://www.example.com/dummy.gif"がそれに相当する)を表示し、そのURLのfragment identfierにJSONデータを分割してエンコードしたものを加える。なおダミーイメージはGData JavaScript が自動的に選択するが、そのためにはあらかじめimgタグが呼び出し元のページに含まれていなければならない。
  • 呼び出し元のページ(www.example.com)では、隠しIFRAMEを作ってリクエストをPOSTした後、そのIFRAMEをタイマーで定期的に監視する。IFRAME内に同一ドメインのURLのIFRAMEが作られた場合はレスポンスが完了しているので、そのfragment identifier値を読み取り、デコードしてオブジェクトに変換する
  • 変換したオブジェクトをレスポンス結果としてGData JavaScript API呼び出し元にコールバックとしてデータを返す

以上。


GDataのパブリックフィードはJSONP形式でも提供しているので、てっきりJavaScript APIJSONPでやっていると思い込んでいました。まあJSONPではリファラしかアクセスコントロールの術がなさそうなので、リファラが使えない時のことを考えるとたしかにこちらの方が提供可能範囲は大きいかもしれない。修正:セキュリティトークンがちゃんと受け渡されている前提ならそんなこともないか。

でも、これと同じことを他のサービサーが真似しようと思うかというと、シンプルさの面でちょっと、という感じがしないでもないので、汎用的なアーキテクチャになりうるかという点ではまだよくわからない。ただ、dojoのXHR IFRAME Proxyを使っていたときは呼び出しクライアント側にIFRAME表示用のHTMLを配置しておく必要があったのでセットアップがちょっとめんどくさく感じたのだけど、これをfragmentの受け渡しを画像URLでやっているというところが案外重要だったりするかもしれない。