Books - XMPP: The Definitive Guide

OReilly から出版されている XMPP: The Definitive Guide を読んだメモ書きです。 XMPP のノード同士におけるデータ交換に関して網羅的に記述されています。 不思議の国のアリスを題材とした具体的な XML が多数例示されていて、どの XEP (XMPP Extension Protocol) が対応するかもポイントしてくれます。 データの雰囲気や用途を知りたい場合はこの本を、より詳しいデータ構造を知りたい場合は XEP を読めば良いはずです。

書籍全体を通して多数の XML が登場しますので、XML の名前空間に関する知識は必須と言えます。 一方で、プロトコルに関する書籍であるために、特定のプログラミング言語に関する知識は必要ありません (とはいえ、何らかのプログラミング経験がないとデータ構造を見ても何も得られないと思いますが)。 最終章でアプリケーションを作ってみる部分では Python を使いますが、ここは読んでも読まなくても好き好きになるでしょう。

XSF の中の人たちが書いていますので、Jabber からの過去の経緯もメモされていて勉強になります。 ネットワークやプロトコル、もしくはデータ構造について詳しく知りたい人は読むべきですが、 ライブラリではなく特定のアプリケーションを実装したい人には非常に遠回りになる書籍です。 目的によって評価がバラバラになる読み物なのではないでしょうか。

構成は大きく三つに分けられます。

An Overview of XMPP:
ざっくりとした概要。歴史やアーキテクチャなど。1章と2章。
The XMPP Toolkit:
XMPP の使いどころ。XMPP で実現できることを一通りさらった感じ。3章から12章まで。
Putting It All Together:
実際にアプリケーションを設計、実装するときに考えること。13章と14章。

Python のサンプルには SleekXMPP を使います。SleekXMPP は Python 3.1 以上もしくは Python 2.6 を使います。 書籍に登場するコードとの差分は wiki にまとめられています。

Preface

XMPP は Jabber が発展したものです。1998-1999年に Jeremie Miller が Jabber をオープンソース化してから、 様々な人たちが関わる中でプロトコルと実装が分離されてきました。 プロトコルは IETF で RFC 3920 (Extensible Messaging and Presence Protocol: Core), RFC 3921 (Extensible Messaging and Presence Protocol: Instant Messaging and Presence) として標準化され、拡張仕様は XEP として文書化されています。 XMPP と Jabber の関係は次の一文で表すことができます。

"XMPP is to Jabber as HTTP is to the Web."

Chapter 1: Introduction

XMPP は次のようなサービスを提供します。

  • Channel encryption
  • Authentication: SASL やサーバーダイアルバックを使う。
  • Presence
  • Contact lists: XMPP サーバでは roster と呼ぶ。
  • One-to-one messaging
  • Multi-party messaging: IRC (Internet Relay Chat) のようなもの。
  • Notifications: PubSub のようなもの。
  • Service discovery
  • Capabilities advertisement
  • Structured data forms: HTML における <form> 要素のようなもの。
  • Workflow management: 順番に処理を実行するもの。データフォームを使う。
  • Peer-to-peer media sessions: Jingle を使う。データ自体のやり取りと管理用のやり取りを分離する。

こうしたサービスを利用することで、アプリケーションを構成できます。 次のようなアプリケーションが考えられます。

Instant messaging, Groupchat, Gaming, System control, Geoloction, Middleware and cloud computing, Data syndication, Voice over IP (VoIP), Identity services (OpenID や OAuth)

Chapter 2: Basics of XMPP

XMPP は "decentralized client-server architecture" であるため、クライアントの開発者はユーザーエクスペリエンスに、 サーバの開発者は信頼性と拡張性に集中できます。"separation of concern" を実現できるのです。

XMPP は E メールシステムのようにドメイン間の接続 (federation) をサポートします。 E メールとの違いは、サーバ間のホップ数がひとつである点です。

page.14 Table 2-1:

Feature Web Email Jabber
Interdomain Connections No Yes Yes
Multiple Hops N/A Yes No

XMPP には、クライアントとサーバ以外に、サーバで特定の処理を実行してくれる "server components" があります。 botcomponents に関しては後で取り上げます。

アドレス

エンティティを識別する文字列を JID (JabberID) と呼びます。ASCII 文字からなり、大文字小文字を区別しません。 E メールアドレスと同じ形式の user@domain.tldbare JID と言います。 接続するクライアントまで考慮してリソースを表現することもできます。 stpeter@jabber.org/roundaboutremko@el-tramo.be/home などです。 リソース部分は大文字小文字を区別しますので、 homeHome は別のリソースです。 user@domain.tld/resourcefull JID と言います。

JID には Unicode を使うこともできます。 RFC 3454 (Preparation of Internationalized Strings) で定義されている stringprep を利用して、 case-folding に注意してください。

ストリーミング XML

XMPP の要点は long-lived TCP で XML を送受信することです。 やり取りする XML は基本的には次のみっつです。 XML stanzas と呼ばれます。

<message/>:情報を送り出す ("push") ための stanza です。 type 属性に "normal", "chat", "groupchat", "headline", "error" を持ち得ます。 to 属性と from 属性に JID を指定して、子要素の <body/><subject/> のメッセージを送ります。
<presence/>:"publish-subscribe" の方法をネットワークへの接続状態に特化したものです。Chapter 3 で詳しく扱います。
<iq/>:Info/Query の省略形で IQ です。 リクエストとレスポンスで情報をやり取りする場合に使います。 type 属性に "get", "set", "result", "error" を持ち得ます。 メッセージがテキストを送るための stanza であるのに対して、IQ はデータを送受信するための stanza です。 レスポンスの type 属性が "error" の場合は、子要素にエラー情報を持ちます。このエラー情報は HTTP や SMTP に由来しますが、 単なる数値ではなく文字列として構成されます。"service-unavailable" や "forbidden" などです。

XMPP で使用される XML の名前空間にはいくつかの命名方法があります。 最初期は jabber:iq:roster で、2001年から2005年頃は http://jabber.org/protocol/muc で、最近は urn:xmpp:jingle のようになっています。 慣習の問題などですので、あまり深く考えない方が良いでしょう。

Chapter 3: Presence

<presence/> 要素でユーザの状態をやり取りします。 <presence/> 要素は <show/><status/> というふたつの基本的な要素を持ちます。

<show/> は次の4つの値のいずれかを持ちます。

  • chat: 会話可能な状態を表現します。
  • away: 一時離席している状態を表現します。
  • xa: "eXtended Away" の略で、かなり長い間、席を外している状態を表現します。
  • dnd: "do not disturb" の略で、邪魔しないでくれ、という状態を表現します。

<status/> 要素は自由な文字列を持つことができます。 例えば次のような組み合わせになります。

<presence>
  <show>away</show>
  <status>Having a spot of tea</status>
</presence>

XMPP はリソースを変えることで複数のクライアントから接続できますので、優先順位を定義できます。 <priority/> 要素に -127 から +128 までを指定できます。

ネットワークにつながった状態から抜ける場合には "unavailable" を指定します。 逆の "available" という指定はありません。

<presence type="unavailable"/>

<status/> を利用して視聴中の音楽情報などを送ることもできます。 これは "rich presence" や "extended presence" と呼ばれます。 しかし <presence/> 本来の使い方からはかけ離れてしまう (意味的にもデータ量的にも) ため、 こうした目的には Publish-Subscribe [XEP-0060] や Personal Eventing Protocol [XEP-0163] (PEP) を使うべきです。

Presence と Rosters

ユーザのコンタクトリスト (rosters) はサーバに保管されます。 クライアントが rosters を取得するためには IQ stanzas を使います。

クライアントが rosters を要求する。

<iq from="alice@wonderland.lit/rabbithole"
    id="jh2gs675"
    to="alice@wonderland.lit"
    type="get">
  <query xmlns="jabber:iq:roster"/>
</iq>

サーバがリストを返す。

<iq from="alice@wonderland.lit"
    id="jh2gs675"
    to="alice@wonderland.lit/rabbithole"
    type="result">
  <query xmlns="jabber:iq:roster">
    <item jid="lory@wonderland.lit"
          name="The Lory"
          subscription="to">
      <group>Wonderlanders</group>
    </item>
    <item jid="mouse@wonderland.lit"
          name="The Mouse"
          subscription="both">
      <group>Wonderlanders</group>
    </item>
    <item jid="queen@wonderland.lit"
          name="Her Royal Highness"
          subscription="from">
      <group>Nobility</group>
    </item>
    <item jid="sister@realland.lit"
          name="Sis"
          subscription="both">
      <group>Family</group>
    </item>
  </query>
</iq>

rosters に含まれる相手の状態が変わった場合にはサーバから通知します。 XMPP では "simple clients, complex servers" (Protocol Design Guidelines [XEP-0134]) が根底にありますので、 難しいことはサーバに任せるようです。

アクセス権に関しては PEP も同様ですので、Chapter 4 と Chapter 8 で扱います。

Chapter 4: Instant Messaging

最初期の Jabber プロジェクト (XMPP と呼ばれる以前) の目標は、オープンなインスタントメッセージングのプラットフォームを構築することでした。 このため、XMPP サーバは比較的小さなメッセージを大量に処理することを前提として設計されています。 このため、イベント駆動方式であり、エンティティはプレゼンスに気をつけることが指針となります。

メッセージの type 属性の基本には次のものがあります。

normal:デフォルトのメッセージタイプで、即座に配送されるかオフラインの場合はサーバに保存されます。
chat:"chat session" と呼ばれる比較的短時間のメッセージです。 ふたつのパーティ間での一対一のコミュニケーションに使われます。
groupchat:複数のユーザが参加するチャットルームです。 Chapter 7 で詳しく扱います。
headline:一時的なメッセージでオフラインには保存されません。
error:以前のメッセージの配送にエラーがあったことを表現します。

チャット相手によっては文字をタイプする速度に差がありますので、会話がうまく噛み合わないこともあります。 電話とは異なり、相手が何をしているかを判別できないからです。 Chat State Notification [XEP-0085] では次の6つの状態を定義します。 <message/> の子要素として表現されます。 50ページの図4-2に状態遷移が示されています。

Starting:会話を始めたけれど、あなたはまだ参加していない状態です。
Active:会話に参加中で、メッセージを書かずに相手に注意を払っている状態です。
Composing:メッセージを書いている状態です。
Paused:メッセージを書いている途中で止まっている状態です。
Inactive:一定時間会話に参加していない状態です。
Gone:会話が終わってしまった状態です。(チャットウィンドウを閉じる、など)

メッセージを装飾したい場合には XHTML-IM [XEP-0071] を使います。 <message/> の子要素として <body/> に加えて <html/> を含みます。 利用可能なタグは HTML よりも制限されています。

vCard-temp [XEP-0054] を使って vCard (RFC2426) をやり取りすることも可能です。

特定の相手とのコミュニケーションを遮断したい場合には IQ-set で <block/> を指定します。 <block/> には <item/><list/> を指定でき、 <item/> の子要素によって詳細な条件を設定できます。 Privacy Lists [XEP-0016] や Simple Communications Blocking [XEP-0191] で定義されています。

Chapter 5: Discovering the World

entity を発見する方法は service discovery あるいは disco と呼ばれます。 entity とは servers, clients, bots, chat rooms, pubsub nodes などを表します。

Service Discovery [XEP-0030] ではふたつの基本的なメソッドを定義します。

disco#items:entity を見つけられるようにします。
disco#info:entity がサポートする機能を見つけます。

クライアントは IQ-get でリクエストを発行し、サーバは IQ-result で結果を返します。 <query/> タグを使いますが、名前空間が http://jabber.org/protocol/disco#itemshttp://jabber.org/protocol/disco#info で意味が異なります。 disco#items で一覧を取得し、その一覧にはアイテムの JID が含まれますので、JID をキーにして disco#info で詳細な情報を取得します。 一覧情報と詳細情報を繰り返し取得することで、木構造を探索していくことができます。

しかし、 disco#info ではデータ量が過多になってしまうこともありますので、プレゼンス情報と組み合わせて Entity Capabilities [XEP-0115] を使うこともあります。 プレゼンス情報は特別なプッシュバージョンの disco#info とも言えます。 Entity Capabilities (名前空間は http://jabber.org/protocol/caps) では、 entity が理解可能な機能 (<feature/>) を連接したハッシュ値をキャッシュしておきます。 ハッシュ値が変わらなければ余計な IQ のやり取りがなくなりますので、ネットワーク負荷を低減できます。

Chapter 6: Data Forms

Data Forms [XEP-0004] は jabber:x:data 名前空間の <x/> 要素以下に構成される HTML の <form/> のようなものです。 データを送信するワークフローとして使われますので、 <x/> 要素は form, submit, cancel, result のいずれかを type 属性に持ちます。 フォームを処理する側 (Processor) とフォームを送信する側 (Submitter) の関係が図6-1にあります。

データ形式は <field/> 要素で定義します。 データ型は type 属性で次のいずれかを指定します。

boolean, fixed, hidden, jid-multi, jid-single, list-multi, list-single, text-multi, text-private, text-single

人間が読むための文字列を定義するためには label 属性を使います。 必須であることを示すためには <required/> 要素を追加します。 CAPTCHA を組み込むこともできます。この場合は urn:xmpp:media-element などの名前空間を使います。 CAPTCHA には画像、クイズ、音声、ビデオなどが使われますが、クライアントが理解できない場合には無視されます。

Chapter 7: Multi-Party Interactions

Multi-User Chat (MUC) [XEP-0045] を使って複数のユーザーがメッセージを交換できるようにします。 "room" という単位で、参加ユーザーは JID ではなくニックネームで識別されます。 "components" と呼ばれるアドオンモジュールはドメインを変えてサービスを提供します。 たとえば、"jabber.org" におけるグループチャットは "conference.jabber.org" のように。

ルームに参加するには <presence/> stanza を送ります。 "to" 属性で指定したニックネームがルーム内で使われます。 ニックネームには Unicode 文字を利用可能です。IRC は US-ASCII に限定されていますので、少し違います。 また、グローバルにユニークである必要はなく、ルーム内で一意であれば構いません。

<presence from="alice@wonderland.lit/rabbithole"
          to="teaparty@conference.wonderland.lit/Alice"/>

メッセージを送信するときには <message/> stanza の "type" 属性に groupchat を指定します。 ルーム内の特定のユーザーと個別にメッセージを交換する場合には chat を指定します。 chat を使うと脇道にそれて個別の会話を進めることができます。

<message from="alice@wonderland.lit/rabithole"
         to="teaparty@conference.wonderland.lit"
         type="groupchat">
    <body>March Hare: There's PLENTY of rooms!</body>
</message>

行儀の悪いユーザーにはルームへの参加を制御することもできます (Crowd Control)。 ユーザーの権限区分には "role" と "affiliation" があり、"role" は一時的な権限、"affiliation" は長期的な権限を意味します。 それぞれ次のいずれかの値を持ちます。

outcast:ルームに参加できない人です。
visitor:ルームに参加して発言を見ることはできますが、発信することはできません。
participant:発言できる人です。
member:特定のメンバーだけのルームに参加できる人です。
moderator:発言に加えて、他の参加者の発言権を変更できます。
admin:発言権の変更に加えて、参加者をルームからはじき出すこともできます。実際の JID を確認し、moderator の任命やルームの設定変更もできます。
owner:全てのルーム設定を変更でき、admin を任命できます。ルームを削除することもできます。

ルームの設定には Data Forms を使います。 FORM_TYPE は "http://jabber.org/protocol/muc#roomconfig" です。 muc#roomconfig_ を接頭辞として、次のような値 (全てを列挙しているわけではない) を設定できます。

allowinvites, changesubject, enablelogging, getmemberlist, lang, maxusers, membersonly, moderatedroom, persistentroom, presencebroadcast, publicroom, roomadmins, roomdesc, roomname, roomowner, whois

Chapter 8: Publish/Subscribe

Publish-Subscribe [XEP-0060] (PubSub) は polling ベースではスケールしないようなアプリケーションに活路を与えてくれます。 IQ stanza を使ってノードへの登録 (subscribing) と発行 (publishing) を行い、message stanza を使ってノードから登録者に通知 (notification) します。 PubSub は XMPP に偏在する考え方ですので XMPP Eventing via PubSub [XEP-0207] というジョークもあります。

ノードがデータを配信する場合には、次の二点を考慮すべきで、これらは設定によって変更できます。

  • ペイロード (データ本体、たとえばブログの本文など) を送信すべきかどうか。
  • ノードがデータを保存すべきかどうか。繰り返し参照や履歴を参照できる (traceability) が、純粋な PubSub パターンからは外れる。

前者は deliver_payloads オプションで設定し、ペイロードを別途取得する場合には <item/>id 属性を辿るようにします。 後者は persist_items オプションで設定します。データを保存しておくノードを persistent nodes, そうでないノードを transient nodes と呼んだりします。

ノードを探すには Chapter 5 で扱った disco を使います。 PubSub のノードの種類には collectionsleaf があります。 ファイルシステムで例えるなら、 collections はフォルダで leaf はファイルです。 collections に対して disco#items で問い合わせると leaf の一覧を取得でき、 leaf に対して disco#info で問い合わせると詳細な情報を取得できます。 ノードのメタ情報 (タイトルや作成者など) を更新するためには Data Forms を使います。

ノードに対するアクセス権として、次の5種類を設定できます。

Open, Whitelist, Authorize, Presence, Roster

pubsub#node_configcollections に子要素を追加および削除できます。 子要素 (leaf) が親要素 (collections) からの参照を切り離すこともできます。

PEP

Personal Eventing Protocol [XEP-0163] (PEP) は MUC (Multi-User Chat) を概念的にさらに簡略化したものです。 サービス専用のノードをユーザーが明示的に探しにいくのではなく、JID が collections のように振舞います。 特定の「人」の活動に着目するため、"lifestreaming" サービスに向いています。

基本的にはリッチプレゼンスを目的としたものでしたが、次のような使用例があります。

User Tune [XEP-0118]:
視聴中の音楽のメタ情報を配信できます。 http://jabber.org/protocol/tune 名前空間を使います。
User Location [XEP-0080]:
ユーザーの位置情報を配信できます。緯度経度高度や、抽象的な住所情報を使えます。 http://jabber.org/protocol/geoloc 名前空間を使います。
User Activity [XEP-0108]:
いくつかのステータス情報と、簡単なテキスト情報を提供します。 http://jabber.org/protocol/activity 名前空間を使います。
User Mood [XEP-0107]:
(Activity とほとんど一緒?) http://jabber.org/protocol/mood 名前空間を使います。
User Nickname [XEP-0172]:
コンタクトリストに表示するためのニックネームを配信します。 他のいくつかのコンテキストに出現することも可能で、 <presence/><message/> に含むこともできます。 http://jabber.org/protocol/nick 名前空間を使います。
User Avatar [XEP-0084]:
ニックネームに加えて画像でユーザーを識別してもらうために使います。 Base64 文字列として画像データをやり取りします。 ネットワーク転送量を浪費しないように、メタデータのハッシュ値 (SHA-1) を使い、変更があった場合のみ実際のデータを転送するようにします。 urn:xmpp:avatar:data 名前空間を使います。

Chapter 9: Jingle: Jabber Does Multimedia

Jingle [XEP-0166] は VoIP (Voice over Internet Protocol) のようにテキスト以外の情報をやり取りする方法です。 Google Talk で使われており、仕様の改善にも大きく貢献してくれました。 XMPP は小さな XML のかけらを大量にやり取りすることには向いていますが、大きなデータのメディアストリームには不向きなのです。 Jingle の基本的なアイデアは、XMPP をセッション管理の signaling チャネルとして使い、 メディア自体のデータは P2P かメディアに特化した方法で送受信します。

initiatorresponder に "Application type" (何を送るか?) と "Transport method" (どうやって送るか?) を伝えます。 セッションの開始、パラメータ変更、終了などは、IQ stanza を使います。

メディア自体を送受信するときに、端末がファイアーウォール越しだったり NAT (Network Address Translators) 越しだったりすると話が複雑になります。 NAT の内外で端末の IP アドレスが変わってしまうためです。 この問題を解決するために STUN (Session Traversal Utilities for NAT), TURN (Traversal Using Relays around NAT), ICE (Interactive Connectivity Establishment) などがあります。 これらをまとめた Jingle ICE-UDP Transport [XEP-0176] もあります。

Jingle のアクションとして次のようなものがあります。通信中に種々の情報を変更することもできます。

session-initiate, session-accept, session-info, transport-info, session-terminate content-add, content-accept, content-reject, content-remove, content-replace, content-modify, description-info, transport-replace, transport-accept, transport-reject

Chapter 10: Sending Binary Data

小さなバイナリデータ (エモーティコンなど) を送信する場合には BOB (Bits of Binary) [XEP-0231] を使います。 "cid:" から始まる URL を使い、BASE64 エンコードされた文字列を IQ-get で受け取ります。 XML の名前空間には urn:xmpp:bob を使います。 データサイズの目安としては 8KB より小さいものです。

デジカメの写真などを扱う場合には IBB (In-Band Bytestreams) [XEP-0047] を使います。 IBB では、データを小さなチャンクに分割して送信します。 ひとつのブロックサイズは 4096 バイトくらいです。 次のような IQ-set stanza を送ってストリームを生成します。

<iq from=alice@wonderland.lit/rabbithole"
    id="iy2s986q"
    to="sister@realworld.lit/home"
    type="set">
  <open sid="dv917fb4"
        block-size="4096"
        xmlns="http://jabber.org/protocol/ibb"/>
</iq>

実際のデータは BASE64 エンコードされ、 <message/> stanza で <data/> 要素を使って送信されます。 <data/> 要素は 0 から始まる seq 属性を持ち、これによって順番を特定します。 全てのチャンクを送信したら <close/> を送ってストリームを閉じます。

<message/> stanza は送りっぱなしの方法なので、順番を担保するために IQ stanza を使うこともできます。 とはいえ、 In-Band の方法ではネットワークトラフィックの増加を避けられませんので、Out-of-Band の方法を使うことも検討します。 (In-Band とは XMPP の世界で閉じた方法で、Out-of-Band とは P2P のように XMPP 以外の方法も使うことを意味します。)

Out-of-Band で大きなデータを送信するためには、エンティティ間で直接転送する方法と、プロキシを介してリレーする方法があります。 前者の場合は SOCKS5 Bytestreams [XEP-0065] (SOCKS5 プロトコルは RFC 1928 で定義されています) を使います。 firewall 越しなどの場合はこれだと通信できませんので、プロキシを使います。 プロキシは disco#info<identity/> 要素の category 属性が proxy です。 XMPP サーバの負荷を分散させるためにも、Out-of-Band での転送は有効です。

ファイル転送の場合には、ネゴシエーションの段階で IBB にするか OOB にするかを選択します。 しかし、その都度選択が必要なことは実装がふたつ存在することにもなりますし、とても煩わしい状況になります。 そこで、Jingle の transport-replace を使って IBB と OOB を切り替える手法が実験的に検討されています。

Chapter 11: Remote Commands

Ad-Hoc Commands protocol [XEP-0050] は XMPP エンティティ同士で何かをやり取りする方法を提供してくれます。 コマンドを要求するエンティティを requester 、コマンドを処理するエンティティを responder と呼びます (本書の図11-1)。 このワークフローは単一のステージでも構いませんし、Data Form を使って複数のステージから成るものでも構いません。 基本的なワークフローは次の手順から成ります。

  1. requesterresponder がサポートしているコマンド一覧を要求します。
  2. responderrequester にコマンド一覧を返します。
  3. requester は 実行するコマンドを responder に発行します。
  4. responder はコマンドを実行してその結果を返すか、さらなるデータを requester に要求します。
  5. 必要があれば requester は追加の情報を responder に渡します。

関係する仕様としては Remote Controlling Clients [XEP-0146]、Service Administration [XEP-0133] があります。 自分で独自コマンドを定義することもできます。

IQ stanza で利用する <command/> 要素 (名前空間は http://jabber.org/protocol/commands) には <actions/> 要素や Data Forms を配置できます。 複数のステージからなるコマンドでは、 <actions/><prev/><next/> を使うことで、 状態遷移を実現できます。

より高度なワークフローを実現するためには SOAP, RPC, IO Data [XEP-244] などを使うこともできます。 HTTP ではなく、XMPP の SOAP バインディングもあります。

Chapter 12: Connection Methods and Security

12章では低レベルな処理を説明します。 接続方法、認証、セキュリティなどの XMPP の基盤をなす部分ですので、 アプリケーション層の実装に注力する場合には読み飛ばしても構わないでしょう。

ざっくりした観点では、クライアントとサーバのセッションは次のようにして確立させます。

  1. クライアントからサーバへ TCP コネクションを開始します。
  2. XML ストリームを開きます。
  3. ストリームの機能をやり取りします。
  4. サーバに対して認証します。
  5. リソースを選択します。
  6. IM アプリケーションの場合は、roster を要求し、最初のプレゼンス情報を送信します。

XMPP サーバを見つけるためには DNS の "A" レコード (もしくは "AAAA" レコード) をルックアップします。 サーバの IP アドレスとポート番号が分かると、クライアントはストリームヘッダを送信します。 タグを閉じていないことに注意してください。 後続する全ての XML はこの要素の子要素として扱われ、セッション終了時に </stream:stream> を送信します。

<?xml version="1.0"?>
<stream:stream to="wonderland.lit"
               version="1.0"
               xmlns="jabber:client"
               xmlns:stream="http://etherx.jabber.org/streams">

サーバはセッション ID を付与してレスポンスヘッダーを返します。 やはり開始タグだけです。

<?xml version="1.0"?>
<stream:stream from="wonderland.lit"
               id="k0d1m43rt53ht"
               version="1.0"
               xmlns="jabber:client"
               xmlns:stream="http://etherx.jabber.org/streams">

この後、クライアントに "stream features" を伝えます。 TLS 可能か、サポートしている SASL メカニズム、圧縮方法が挙げられます。

SASL 認証に成功すると、サーバは新しいセッション (新しいセッション ID) を開始させます。 ここでのサーバからのレスポンスには SASL に関するものは含まれません。 代わりに "resource binding" が含まれます。 リソースバインドが完了するとログイン状態になりますので、IQ stanza を使って roster の要求とプレゼンス情報の初期化を行います。

SASL のメカニズムには次のものがあります。 サーバ間通信では EXTERNAL が使われるそうです。

PLAIN, DIGEST-MD5, SCRAM, EXTERNAL, GSSAPI, ANONYMOUS

経路の暗号化とサーバの身元証明のためには TLS を使います。 <starttls/> タグ (名前空間は urn:ietf:params:xml:ns:xmpp-tls) で、通常の接続経路から切り替えられます。 TLS で繋ぎなおすと、セッションをやり直します。

Server Components

  • Jabber Component Protocol [XEP-0114]
  • Component Connections [XEP-0225]

Server Federation

サーバ同士の通信は "federation" と呼ばれます。server-to-server なので "s2s" と呼ばれることもあります。

s2s では認証に前提知識を要求できませんので、SASL メカニズムの多くは利用できません。 TLS + SASL EXTERNAL の組み合わせは強力な認証手段なのですが、広く普及していませんので、気軽に使うことができません。 TLS で行われる証明書交換を利用できない場合は、ややセキュリティ的に劣る "server dialback" を使います。

server dialback は、電力会社の社員がお宅を訪問してきたときに、その会社に社員番号を問い合わせることに似ています (訳注: 訪問者の身元確認をせず、いきなり家に上げたりしない、という点が日本とは文化的に異なるかも)。 この場合、家主が receiving server で、電力会社の社員が originating server に当たります。 電力会社に問い合わせるためには電話帳を調べることになりますが、これは DNS (Domain Name System) に相当します。 そして、電力会社で従業員 ID を照会する部門が authoritative server と言えます。 フロー図は本書の図12-1。

server dialback も悪くはありませんが、DNS が汚染されてしまうと打つ手がなくなってしまいます。 TLS + SASL EXTERNAL の機構を使い、証明書ベースで認証できるようにしていけるのが望ましい方向性です。 現実の世界では、CA (Certification Authority) から証明書を発行してもらうためにはお金がかかりますので、 自己証明書を使ってしまうことが多いことも問題です。 XMPP Standards Foundation では無料の証明書も検討していますので、 http://xmpp.org/ca/ も参照してください。

BOSH: XMPP over HTTP

BOSH (Bidirectional-streams Over Synchronous HTTP) は long-lived TCP を使えない環境で有効です。 HTTP を使うために既存のプラットフォームに統合しやすいでしょう。 Comet に似ていますが、すこし違います。 コアとなる仕様は XEP-0124 で定義され、追加のルールや実際に考慮すべきことは XEP-0206 にあります。

BOSH では "connection manager" (CM) と呼ばれる特別なプロキシを導入します。 XMPP サーバとは通常の XMPP の XML をやり取りし、HTTP クライアントとは HTTP で <body/> 要素をラップしたものをやり取りします。

たとえば CM が次の DNS レコードを持つとします。

_xmppconnect IN TXT "_xmpp-client-xbosh="https://bosh.wonderland.lit/webclient"

クライアントは次の HTTP リクエストを CM に投げます。 以後のやり取りは <body/> 要素の中に <stream:features/><iq/> あるいは <message/> を含めるようにします。

POST /webclient HTTP/1.1
Host: bosh.wonderland.lit
Content-Type: text/xml; charset=utf-8
Content-Length: 181

<body hold="1"
      secure="true"
      rid="90029201"
      to="wonderland.lit"
      wait="60"
      xmpp:version='1.0"
      xml:lang="en"
      xmlns="http://jabber.org/protocol/httpbind"
      xmlns:xmpp="urn:xmpp:xbosh"/>

基本的には HTTP ですから、リクエストとレスポンスのペアを管理します。 しかし、あるクライアントが連続してメッセージを送信することもありますし、タイムアウトを管理する必要もあります。 CM はこういった接続管理のために空の <body/> 要素をレスポンスとして返したりします。 複数のリクエスト / レスポンスのペアを管理するフローは図12-3にあります。 (この辺は実装の詳細などは非常にややこしそう。。)

Serverless Messaging

Serverless Messaging [XEP-0174] は Apple が開発した "zero-configuration networking" を基にして策定されました。 .local という TLD (Top Level Domain) を使います。 DNS の PTR、A、SRV レコードで定義される presence サービスを使っているエンティティを発見します。 詳しくは mDNS (Multicast DNS) や DNS-SD (DNS-based service discovery) を参照してください。

XMPP Secrity

「セキュリティ」とひと口に言えども共通見解はありません。 とはいえ、専門家が一般的な脅威と認めるものもあります (Page.193 に列挙)。 Encryption, Authentication, Spam, Abuse に関しては一通り考慮しましょう。

NOTE:

  • Security can mean different things to different people.
  • the lack of successful attacks is no reason to be complacent.

Chapter 13: Design Decisions

Marshall Rose:
you can build a good helicopter or you can build a good submarine, but you can't build something that is both a good helicopter and a good submarine.
the key principles of protocol desing in the Jabber community (Page.208) :
The core XMPP specifications are sacred. Any feature you might need can probably be defined farther up the XMPP protocol stack than in the core streaming, messaging, and presence layer (and if you think that you need to modify the core layer, think again). Instead focus higher up the stack by defining new namespaces that can be included in message or IQ stanzas (and, rarely, presence stanzas).

既存の機能を最大限に再利用しましょう、と。 付け足して欲しい拡張機能があれば、コミュニティに提案して仕様にしましょう、と。

Chapter 14: Building an XMPP Application

Python (ライブラリには SleekXMPP) を使い、スプリント形式で機能を改善していきます。 例として "CheshiR" というアプリケーションを実装します。 内容は本書を読む、ということで。

  • First Sprint: The CheshiR XMPP IM Bot
  • Second Sprint: Configuring the CheshiR XMPP IM Bot
  • Third Sprint: Scaling the CheshiR XMPP Service Using a Server Component
  • Fourth Sprint: Registering with the CheshiR Server Component
  • Fifth Sprint: Extending the Server Component with Rosters

Appendix A: Guide to XMPP Specifications

RFC3920 と RFC3921 が基本的な仕様ですが、改訂版の RFC3920bis と RFC3921bis もあります。 XMPP 独自の拡張は XEP で定義されます。

XEP には次の5種類があります。

Standards Track, Informational, Historical, Humorous, Procedural

それぞれのドキュメントは以下の状態のどれかを持ちます。

Experimental, Proposed, Deferred, Retracted, Deprecated, Obsolete, Rejected Active, Draft, Final

Appendix C: Further Practical Considerations

その他

2010年8月末から読み始めて2010年12月末に読了。

Google App Engine でも使える。

Google Wave ではノード間のデータ通信 (federation) に XMPP を使っていたが、XMPP から HTTP に変更した。 (Progress Report) 逆に Diaspora では XMPP を使おうという話もあった。