转载:http://hi.baidu.com/zqhhshs/blog/item/495df024c2c77920d5074253.html
1.介绍
BOSH协议定义了如何能够有效并可靠地通过HTTP在客户端和服务器之间双向传输任意XML元素。本文档定义了一些BOSH的小扩展来将XMPP流绑定到HTTP上。
2.<body/>元素
如果BOSH的<body/>元素不为空,它应该包含以下内容之一:
* 一个完整的<stream:features/> 元素 (此时BOSH <body/> 元素一定要包含命名空间 xmlns:stream='http://etherx.jabber.org/streams')。
* 一个用于通过SASL验证的完整元素,并且要由'urn:ietf:params:xml:ns:xmpp-sasl'命名空间认定。
* 一个或者多个完整的<message/>, <presence/>, 和/或者 <iq/> 元素,由'jabber:client'命名空间认定。
* 一个<stream:error/>元素(在这种情况下, BOSH <body/>元素一定要包含命名空间 xmlns:stream='http://etherx.jabber.org/streams' 并且它一定要着重说明'remote-stream-error' 终端错误的条件),在它之前可以有0个或多个完整的<message/>, <presence/>, 和/或者 <iq/> 元素,由'jabber:client'命名空间认定。
注 意:许多BOSH客户端和连接管理器现有的特殊XMPP实现没有指定<message/>,<presence/>,或 者<iq/>元素的命名空间,因为它允许它们传输不经修改的数据节(XMPP <stream:stream/> 元素和TCP一起代表性地设置了默认的命名空间'jabber:client')。作为替代,它们只是简单地假定'jabber:client'命名空间 的所有内容都是'http://jabber.org/protocol/httpbind'命名空间的子集。
注意:可以包含TLS验证元素,但是并不推荐。有关TLS如何通过BOSH来实施的内容目前已经超过了本文档的范围。作为替代,通道加密应该在HTTP传输层完成,而不是在XMPP应用层。
3.会话创建请求
客 户端应该在会话创建请求中包含由'urn:xmpp:xbosh'命名空间认定的'version'属性。这个属性相当于定义在RFC 3920文档中XMPP <stream:stream/>元素的'version'属性。连接管理器应该相应地将其值传给XMPP服务器。
例1 使用version属性请求会话
POST /webclient HTTP/1.1
Host: httpcm.jabber.org
Accept-Encoding: gzip, deflate
Content-Type: text/xml; charset=utf-8
Content-Length: 104
<body content='text/xml; charset=utf-8'
hold='1'
rid='1573741820'
to='jabber.org'
route='xmpp:jabber.org:9999'
secure='true'
wait='60'
xml:lang='en'
xmpp:version='1.0'
xmlns='http://jabber.org/protocol/httpbind'
xmlns:xmpp='urn:xmpp:xbosh'/>
注意:与Jabber HTTP Polling中的定义的协议不同,一个开放的<stream:stream>标签并没有发送给连接管理器(因为BOSH <body/>元素一定不能包含部分XML元素)。连接管理器负责管理任何在它和XMPP服务器之间的数据流(它超越了本文档的范围)。
4.会话创建响应
连接管理器一旦收到服务器的数据,应该在它的会话创建响应或是任何后继响应中包含一个'version'属性(由'urn:xmpp:xbosh'命名空 间认定)和一个<stream:features/>元素(由'http://etherx.jabber.org/streams'命名空 间认定)。
注意:同样的程序也应用于BOSH <body/>元素中过时的特殊XMPP 'authid'属性,它包含了由XMPP服务器产生的XMPP流ID的值。这个值只在旧系统的XMPP客户端使用过时的非SASL认证协议完成摘要认证 时使用。
例2 具有流特征的会话创建响应
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: 674
<body wait='60'
inactivity='30'
polling='5'
requests='2'
hold='1'
accept='deflate,gzip'
sid='SomeSID'
secure='true'
charsets='ISO_8859-1 ISO-2022-JP'
xmpp:version='1.0'
authid='ServerStreamID'
xmlns='http://jabber.org/protocol/httpbind'
xmlns:xmpp='urn:xmpp:xbosh'
xmlns:stream='http://etherx.jabber.org/streams'>
<stream:features>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism>
</mechanisms>
</stream:features>
</body>
如果在连接管理器的会话创建响应中没有<stream:features/>元素,客户端应该发送空的请求元素,直到它收到含有<stream:features/>元素的响应为止。
例3 具有流特征的后继响应
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: 483
<body xmpp:version='1.0'
authid='ServerStreamID'
xmlns='http://jabber.org/protocol/httpbind'
xmlns:xmpp='urn:xmpp:xbosh'
xmlns:stream='http://etherx.jabber.org/streams'>
<stream:features>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism>
</mechanisms>
</stream:features>
</body>
注意:客户端应该忽略任何传输层安全(TLS)特性,因为BOSH的通道加密应该在HTTP层讨论。
不推荐TLS压缩(在RFC 3920中定义)和流压缩(在流压缩中定义[7]),因为压缩应该使用BOSH会话创建响应中的'accept'属性在HTTP层协商。TLS压缩和流压 缩不应该在HTTP内容编码的同时使用。
注意:当添加新数据流到会话中时,由'urn:xmpp:xbosh'命名空间认定的'version'属性也应该包含在请求和响应中。
5. 认证,源绑定和即时通讯会话的建立
以下是成功的使用XMPP协议进行认证和源绑定的实例。需要这些协议的细节说明(包括错误实例),请参考RFC 3920。
例4 SASL认证步骤1
POST /webclient HTTP/1.1
Host: httpcm.jabber.org
Accept-Encoding: gzip, deflate
Content-Type: text/xml; charset=utf-8
Content-Length: 172
<body rid='1573741821'
sid='SomeSID'
xmlns='http://jabber.org/protocol/httpbind'>
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>
</body>
例5 SASL认证步骤2
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: 250
<body xmlns='http://jabber.org/protocol/httpbind'>
<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
cmVhbG09InNvbWVyZWFsbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9
ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNzCg==
</challenge>
</body>
例6 SASL认证步骤3
POST /webclient HTTP/1.1
Host: httpcm.jabber.org
Accept-Encoding: gzip, deflate
Content-Type: text/xml; charset=utf-8
Content-Length: 418
<body rid='1573741822'
sid='SomeSID'
xmlns='http://jabber.org/protocol/httpbind'>
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
dXNlcm5hbWU9InNvbWVub2RlIixyZWFsbT0ic29tZXJlYWxtIixub25jZT0i
T0E2TUc5dEVRR20yaGgiLGNub25jZT0iT0E2TUhYaDZWcVRyUmsiLG5jPTAw
MDAwMDAxLHFvcD1hdXRoLGRpZ2VzdC11cmk9InhtcHAvZXhhbXBsZS5jb20i
LHJlc3BvbnNlPWQzODhkYWQ5MGQ0YmJkNzYwYTE1MjMyMWYyMTQzYWY3LGNo
YXJzZXQ9dXRmLTgK
</response>
</body>
例7 SASL认证步骤4
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: 190
<body xmlns='http://jabber.org/protocol/httpbind'>
<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZAo=
</challenge>
</body>
例8 SASL认证步骤5
POST /webclient HTTP/1.1
Host: httpcm.jabber.org
Accept-Encoding: gzip, deflate
Content-Type: text/xml; charset=utf-8
Content-Length: 152
<body rid='1573741823'
sid='SomeSID'
xmlns='http://jabber.org/protocol/httpbind'>
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
</body>
例9 SASL认证步骤6
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: 121
<body xmlns='http://jabber.org/protocol/httpbind'>
<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
</body>
在收到元素以后,客户端一定还要让连接管理器通过发送“重新开始请求”来重新发送数据流,“重新开始请求”的结构如下:
* BOSH <body/> 元素一定要包含布尔型的'restart'属性,(由'urn:xmpp:xbosh'命名空间认定) 其值为真。
* BOSH <body/> 元素应该包含'to'属性。
* BOSH <body/> 元素应该包含'xml:lang'属性。
* BOSH <body/> 元素应该为空 (比如不能包含XML数据节)。然而,如果客户端在body中包含了XML数据节,连接管理器应该忽略它。
下面的例子说明了重新开始请求的格式。
例10 重新开始请求
POST /webclient HTTP/1.1
Content-Type: text/xml; charset=utf-8
Content-Length: 240
<body rid='1573741824'
sid='SomeSID'
to='jabber.org'
xml:lang='en'
xmpp:restart='true'
xmlns='http://jabber.org/protocol/httpbind'
xmlns:xmpp='urn:xmpp:xbosh'/>
在收到一个重新开始请求后,连接管理器一定要考虑之前由即将关闭的XMPP服务器发送的数据流。之后它一定要在同样的TCP连接上发送一个开放的<stream:stream>标签到XMPP服务器来初始化一个新的数据流。如果连接管理器收到一个来自XMPP服务器的<stream:features/>元素,它一定要将这个元素发送到客户端:
例11 重新发送请求的
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: 221
<body xmlns='http://jabber.org/protocol/httpbind'
xmlns:stream='http://etherx.jabber.org/streams'>
<stream:features>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
</stream:features>
</body>
客户端之后能完成任何强制的或随意的数据流特性协商。
例12 源绑定请求
POST /webclient HTTP/1.1
Content-Type: text/xml; charset=utf-8
Content-Length: 240
<body rid='1573741825'
sid='SomeSID'
xmlns='http://jabber.org/protocol/httpbind'>
<iq id='bind_1'
type='set'
xmlns='jabber:client'>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
<resource>httpclient</resource>
</bind>
</iq>
</body>
例13 源绑定结果
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: 221
<body xmlns='http://jabber.org/protocol/httpbind'>
<iq id='bind_1'
type='result'
xmlns='jabber:client'>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
<jid>stpeter@jabber.org/httpclient</jid>
</bind>
</iq>
</body>
6. 远程数据流错误
<body/>元素的内容是来自XMPP服务器的0个或多个数据节,伴随着一个<stream:error/>元素(由'http://etherx.jabber.org/streams'命名空间认定)复本:
例14 远程错误
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: 68
<body condition='remote-stream-error'
type='terminate'
xmlns='http://jabber.org/protocol/httpbind'
xmlns:stream='http://etherx.jabber.org/streams'>
<message from='contact@example.com'
to='user@example.com'
xmlns='jabber:client'>
<body>I said "Hi!"</body>
</message>
<stream:error>
<xml-not-well-formed xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
<text xmlns='urn:ietf:params:xml:ns:xmpp-streams'
xml:lang='en'>
Some special application diagnostic information!
</text>
<escape-your-data xmlns='application-ns'/>
</stream:error>
</body>
7. 接收者不存在或断线
即使客户端的连接不存在了,连接管理器也有可能收到要发送给客户端的数据节(例如,在连接管理器通知XMPP服务器客户端的连接已断之前)。在这种情况下,连接管理器会向XMPP服务器返回一个错误信息,我们建议连接管理器进行以下操作,因为它与RFC 3921文档第11部分的第2点中的情况类似:
- 如果被传递的数据节是<presence/>,悄悄地抛弃这个数据节并且不向发送者返回错误信息。
- 如果被传递的数据节是 <iq/>,向发送者返回一个 <service-unavailable/>错误。
- 如果被传递的数据节是 <message/>,向发送者返回一个<recipient-unavailable/>错误。
当XMPP服务器收到来自连接管理器的包含<recipient-unavailable/>错误的<message/>数据节时,它应该将这个消息保存起来,以备日后支持线下存储 (参见 Best Practices for Handling Offline Messages [11])时的信息传递,否则应将这个错误数据节返回给发送者。