------------恢复内容开始------------
原文来自:https://xmpp.org/extensions/xep-0198.html,只翻译了技术方面的内容。
摘要:这个规范定义了一个XMPP协议扩展,用于在两个XMPP实体之间对XML流进行主动管理,包括stanza确认和流恢复的特性。
作者:Justin Karneges,Peter Saint-Andre,Joe Hildebrand,Fabio Forno,Dave Cridland,Matthew Wild
状态:草案。
注意:这里定义的协议是XMPP标准基金会的一个标准草案。我们鼓励实现这个协议,协议适合在生产系统中部署,但是在协议变成最终标准前可以对其进行一些修改。
类型:Standards Track(可参考RFC 2026)
版本:1.6(2018-07-25)
1.介绍
XMPP Core定义了XMPP使用的基本流XML技术(例如:流的建立和终止,包括认证和加密)。但是,核心XMPP规范不提供工具主动管理活跃的XML流。
流管理背后的基本概念是发起实体(客户端或服务端)和接收实体(服务器)能够交换“命令”来主动管理流。下面流管理的功能特别值得关注,因为它们有望提升网络可靠性和最终的用户体验:
Stanza确认:知道一个或一些列stanza是否被一端接收的能力。
流恢复:快速恢复已经被终止流的能力。
流管理在流的根等级使用简短XML元素实现了这些功能。这些元素不是XMPP场景里的stanza(例如:不是在http://wiki.jabbercn.org/RFC6120定义的<iq/>,<message/>,<presence/>stanza),并且不被计数或在流管理中确认,因为它们的存在目的就是为了管理stanza本身。
流管理在XML流等级中被使用,为了检查给定流下的TCP连接,建议使whitespace keepalives(RFC 6120),XMPP Ping(XEP-0199)或TCP keepalives。与流管理相比,高级消息处理(XEP-0079)和消息交换凭证(XEP-0184)定义了在多个流发送点对点的确认。这些工具在特殊场景中很有用,但是对于检查两个XMPP实体之间的直接流来说是不需要的。
注意:流管理可以用在服务器到服务器之间的流上,也可以用在客户端到服务器的流上。但是,这个规范只讨论客户端到服务器的流。同样的原则也适合于服务器到服务器的流。
2.流特性
服务器向客户端返回携带流特性的流头部,流特性包括一个由命名空间'urn:xmpp:sm:3'限定的<sm/>元素(命名空间版本的增加请查阅下面的“命名空间版本控制”)。
注意:客户端在与服务器进行身份认证并绑定资源前不能协商流管理,具体限制见下文。
Example 1:服务器发送伴随流特性的流头部 <stream:stream from='example.com' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'> <stream:features> <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/> <sm xmlns='urn:xmpp:sm:3'/> </stream:features>
3.启用流管理
为了启用流管理,客户端发送<enable/>命令给服务器。
Example 2:客户端允许流管理 <enable xmlns='urn:xmpp:sm:3'/>
如果客户端希望被启用恢复流,必须要包含一个布尔类型的'resume'属性,默认为false。有关恢复一个前面会话的信息,请查看文档的恢复章节。
<enable/>元素或许包含一个'max'属性,用来指定首选的每秒最大恢复时间。
在接收到enable请求后,服务器必须回复一个携带'urn:xmpp:sm:3'命名空间的<enabled/>元素或一个携带'urn:xmpp:sm:3'的<failed/>元素。<failed/>元素表示建立流管理会话时出现了问题。<enabled/>元素表示成功建立流管理会话。
Example 3:服务器允许流管理 <enabled xmlns='urn:xmpp:sm:3'/>
接下来参与者就可以使用下面定义的流管理特性了。
如果服务器允许会话恢复,服务器必须包含一个设置为‘true’或‘1’的‘resume’属性。
Example 4:服务器允许会话恢复的流管理 <enabled xmlns='urn:xmpp:sm:3' id='some-long-sm-id' resume='true'/>
<enabled/>元素或许包含'max'属性来指定服务器首选的最大恢复时间。
<enabled/>元素或许包含'location'属性来指定服务器首选重连的IP地址或主机名(最好是带上端口号),在RFC 6120的Section 4.9.3.19中制定了形式(例如:“域名:端口”,IPv6地址以方括号包围"[...]",这在RFC 5952中有定义)。如果重连到指定地址失败,会使用在RFC 6120指定的标准XMPP连接算法。
客户端不能在认证前尝试协商流管理,例如:客户端不能在认证成功完成前发送<enable/>元素。(认证包括SASL,Non-SASL Authentication (XEP-0078) [7] 或 Server Dialback (XEP-0220))
对于客户端到服务器的连接,客户端在完成资源绑定前不能尝试启用流管理,除非它正在恢复前一个会话。
服务将强制执行此命令,如果违反则响应<failed/>元素。
Example 5:客户端在绑定资源前尝试启动流管理时,服务器返回error <failed xmlns='urn:xmpp:sm:3'> <unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </failed>
请注意,客户端最多只能尝试一次启动流管理,如果服务端接收到第二个<enable/>元素,服务端必须响应流错误,从而终止客户端连接。
Example 6:如果客户端不止一次尝试启动流管理,服务器返回错误 <failed xmlns='urn:xmpp:sm:3'> <unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </failed>
4.确认
在启动流管理后,客户端或服务器可以在任何时候通过流发送ack元素,ack元素可以是下面的任意一种:
<a/>元素被用来响应确认请求或发送未请求的确认。
<r/>元素被用来为接收到的stanza请求确认。
属性定义如下:
'h'属性标识了最后一个处理过的stanza(例如:服务器接收后确认的最后一个stanza)
<a/>元素必须具备一个'h'属性。
<r/>元素没有定义的属性。
定义:确认一个以前接收的ack元素表示从这之前的stanza都被服务器处理了。我们说的“已处理”是指服务器接收到了stanza的职责(例如,直接处理stanza,或传递stanza给一个本地实体,如同一服务器的另一个连接客户端,或者路由stanza到不同服务器的远程实体)。在stanza被服务器确认为处理之前,该stanza由发送方负责(例如,如果stanza从未被服务器确认为处理时,重新发送或生成一个错误)。
接收到<r/>元素并不意味着新的stanza已经被传输到新的端,接收到<a/>并且'h'属性增加了只表示新的stanza已经被处理了。
在流管理被启动或被请求启动的时间点,'h'的值从0开始。在第一个stanza被处理后'h'值被增加,并且在处理每个后续stanza后都加一。在不太可能的情况下,流管理会话期间处理的stanza数量超过了unsignedint数据类型的数据范围,范围在XML Schema Part 2有指定(最大可为4294967295),‘h’的值将从max-1被重置为0,而不是增加到max。
注意:每个实体为给定的流维持了两个计数器:一个是已发送stanza的计数器,一个是已经接收并处理('h')的计数器。实体的发送stanza计数器在发送<enable/>或<enabled/>后设置为0并启动。接收stanza的计数器在接收到<enable/>或<enabled/>元素后设置为0并启动。
以下带注释的例子显示了客户端发送的消息,请求确认和stanza的确认。
Example 7:简单的stanza确认 <!-- 客户端 --> <enable xmlns='urn:xmpp:sm:3'/> <!-- 客户端将发送计数器设置为0. --> <message from='laurence@example.net/churchyard' to='juliet@example.com' xml:lang='en'> <body> I'll send a friar with speed, to Mantua, with my letters to thy lord. </body> </message> <!-- 注意客户端要等待请求结果 --> <!-- 服务器发送给客户端 --> <enabled xmlns='urn:xmpp:sm:3'/> <!-- 服务器接收到启动和响应,设置发送和接收计数器为0 并且,客户端设置接收计数器为0 --> <!-- 客户端发送给服务端请求确认 --> <r xmlns='urn:xmpp:sm:3'/> <!-- 服务端发送给客户端确认请求 --> <a xmlns='urn:xmpp:sm:3' h='1'/>
当接收到一个<r/>元素时,接收方必须发送包含'h'属性的<a/>元素给发送方来确认,'h'元素等于接受<r/>元素接受方处理stanza的数量。在接收到<r/>后应该尽快发送响应,并且除了超时外,不能因为任何条件拒绝响应。例如,连接速度比较慢的客户端希望在确认前收集到一段时期的很多stanza,而服务器希望限制传入的stanza。发送方不需要等待确认后继续发送stanza。
任何一方都可以在任何时候发送<a/>元素(例如:在收到确定数量stanza后,或在确定的时间后),即使没有从其它方接收到<r/>元素。建议发起实体(一般是客户端)在优雅关闭流之前发送一个<a/>元素,以便将接受到的stanza通知另一端。否则可能会重新发送stanza(一般是服务端),尽管它们实际上已经被接收。
当一方接收到一个<a/>元素,它应该保留返回的'h'属性值,这个值是当前流的上一个已被处理的发送类型stanza(并且放弃前一个值)的序列号。
如果一个流结束了并且没有在<enabled/>元素指定的时间内恢复,序列号和任何相关状态或许会被双方丢弃。在丢弃会话状态之前,实现类应该对未处理的stanza采取其它操作(例如:stanza在最近收到的'h'值后发送)。
服务器应该像处理发送到不可用资源的stanza一样处理未确认的stanza,或是返回错误给发送者,或是提交stanza给备用资源,或是提交stanza给离线存储。(注意:服务器应该根据延迟发送(XEP-0203)增加一个带有发送时间戳的延迟元素)
面向用户的客户端应该尝试在重新连接时静默重发stanza,或者通过适当的用户界面元素通知用户失败了。客户端还应该添加一个携带时间戳的延迟元素,以便保留原始的发送日期。否则接收方的客户端只能显示发送方的重新连接时间戳,这可能会使用户感到困惑。
因为未确认的stanza可能被另一方接收了,重新发送可能会导致重复,在本协议中没有办法避免这个结果,虽然使用在所有stanza上的id属性可以帮助接收方处理重复的stanza。
5.恢复
这在XML流在非预期情况下被终止。(例如:网络中断)在这种情况下,最好是快速恢复以前的流,而不是通过完成流建立,名单检索和状态广播的繁琐处理流程。
此外,此协议交换上一次连接接收到的最后一次stanza序列号,允许实体确定哪些stanza需要重传,哪些stanza不需要重传,从而通过重放消除重复。
为了使请求的流是可恢复的,当启用流管理时,客户端必须在<enable/>元素添加一个'resume'属性,值为'true'或'1'。
Example 8:客户端启动流管理 <enable xmlns='urn:xmpp:sm:3' resume='true'/>
如果服务器允许恢复流,服务器必须包含一个设置为'true'或'1'的resume属性在<enable/>元素,并且必须包含一个指定流标识的id属性。
Example 9:服务端允许流恢复 <enabled xmlns='urn:xmpp:sm:3' id='some-long-sm-id' resume='true'/>
定义:id属性定义了一个用于流管理的唯一标识符(SM-ID)。SM-ID必须由服务端生成。客户端必须把SM-ID当成是不透明的,因此不能赋予SM-ID任何语义。服务端可以将认为有用的任何信息编码到SM-ID中,例如连接客户端的完整JID<localpart@domain.tld/resource>(例如full JID加随机值)。任何字符都允许加到XML属性中。SM-ID不能同时或在后续的对话中重用(但是服务端不需要确保SM-ID在任何使用都是唯一的,只要在服务器持续运行的这段时间即可)。SM-ID不能超过4000字节。
<enabled/>元素可能包含一个'location'属性,该属性定义了服务器的首选重连location(例如,为连接客户端保持会话状态的特定连接管理器)
Example 10:服务器倾向于在特定位置重新连接 <enabled xmlns='urn:xmpp:sm:3' id='some-long-sm-id' location='[2001:41D0:1:A49b::1]:9222' resume='true'/>
如果流意外终止,客户端会打开到服务器的TCP连接,事件顺序如下:
(1)断开连接后,客户端打开一个新的到服务器的TCP连接,倾向于使用location属性指定的地址(如果存在)。
(2)客户端发送启动流头部。
(3)服务端发送响应流头部。
(4)服务端发送流特性。
(5)客户端发送STARTTLS请求。
(6)服务端通知客户端进行TLS协商。
(7)双方完成TLS握手。(注意:在执行会话恢复和使用TLS时,建议使用TLS会话恢复(RFC-5077)来进一步优化XML流的恢复)
(8)客户端发送新的启动流头部。
(9)服务端发送响应流头部。
(10)服务端发送流特性,需要SASL协商并提供适当的SASL机制。(注意:如果服务器认为在TLS会话恢复过程中提供的信息已足够认证,服务端会提供SASL外部机制,详情可查阅draft-cridland-sasl-tls-sessions)
(11)双方完成SASL协商。
(12)客户端发送新的启动流头部。
(13)服务端发送响应流头部。
(14)服务端发送流特性,提供SM特性。
(15)客户端请求恢复前面一个流。
注意:事件的顺序可能与上面显示的不同,这取决于服务端何时提供SM特性,客户端是否选择STARTTLS等等。此外,在实践中,服务端到服务端通常不完成SASL协商,甚至不完成TLS协商。上述文本不修改在RFC 6120中关于流协商过程中的任何规则。但是,由于流管理被应用于stanza的交换(不是任何其它的XML元素),所以当另一方能够开始发送stanza时(而不是之前),服务端提供的SM特性是有意义的。See also Recommended Order of Stream Feature Negotiation (XEP-0170)
为了请求恢复前一个流,客户端发送一个限定'urn:xmpp:sm:3'命名空间的<resume/>元素。<resume/>元素必须包含一个'previd'属性,它的值是上一个流的SM-ID,同时必须包含一个'h'属性,为了标识上一个从服务端到客户端的流的最后一个已处理stanza的序列号(在客户端没有收到任何stanza时,h为0)。
Example 11:流恢复请求 <resume xmlns='urn:xmpp:sm:3' h='some-sequence-number' previd='some-long-sm-id'/>
如果服务端能够恢复上一个流,服务端必须返回一个<resumed/>元素,<resume/>元素必须包含一个'previd'属性,它的值是上一个流的SM-ID,同时必须包含一个'h'属性,为了标识上一个从服务端到客户端的流的最后一个已处理stanza的序列号(在客户端没有收到任何stanza时,h为0)。
Example 12:流恢复 <resumed xmlns='urn:xmpp:sm:3' h='another-sequence-number' previd='some-long-sm-id'/>
如果服务器不支持会话恢复,它必须返回一个<failed/>元素,<failed>元素应该包含一个<feature-not-implemented/>的错误条件。如果服务器不将'previd'识别为一个早期的会话(例如:因为前一个会话已经超时),服务器必须返回一个<failed/>元素,它包含一个<item-not-found/>的错误条件。如果服务端将'previd'识别为一个早期会话并且这个会话超时了,服务端可能包含一个'h'属性,表示在超时前接收到的stanza编号。(注意:为了让其有效,服务端只能将SM-ID/序列号的元祖保存在实际会话之外)
Example 13:流超时 <failed xmlns='urn:xmpp:sm:3' h='another-sequence-number'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </failed>
在这些失败情况下,服务器应该允许客户端在此时绑定资源,而不是轻质客户端重启流协商过程和重新认证。
如果前一个流被恢复,并且此时服务端还有之前识别会话的流处于打开状态,服务器应该发送'conflict'流并且关闭流。
当一个会话被恢复后,各方按下列步骤进行:
序列值从上一个会话传递下来并且不会为新的流重置。
在接收到<resume/>或<resumed/>元素是,客户端和服务端使用'h'属性来重新传输因为断开连接丢失的所有stanza。实际上,它应该处理元素的'h'属性,就像处理<a/>元素一样(例如:在发送队列中将stanza标记为已处理),除非在处理后,它必须重发给另一端任何被标记为未处理的stanza。
双方应该重发在前一个会话期间未处理的任何stanza,这要依据对方报告的序列号。
重新连接的客户端不应该请求名单,因为在客户端断开连接所有名单的修改都会在流管理会话恢复后发送给客户端。
客户端不应该重新发送状态stanza来尝试恢复到以前的presence状态,因为服务器会保留此状态。
双方都不应该尝试重新建立状态信息。(例如 Service Discovery (XEP-0030)信息)
6.错误处理
如果关于<enable/>和<resume/>元素发生错误,服务器必须包含一个错误条件,它必须是RFC 6120中定义的其中一个stanza错误条件。
例子如下:
Example 15:服务端返回错误 <failed xmlns='urn:xmpp:sm:3'> <unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </failed>
流管理错误应该被认为是可恢复的,无用流管理可能会导致流的终止。
当一个远程实体确认它已处理的stanza数量比发送的stanza数量更多(通过发送一个很大的'h'值),本地实体因该生成一个undefined-condition流错误,它包含了一个<handled-count-too-high/>元素,并且关闭流。
Example 16:由于一端确认的stanza比发送的更多 <stream:error> <undefined-condition xmlns='urn:ietf:params:xml:ns:xmpp-streams'/> <handled-count-too-high xmlns='urn:xmpp:sm:3' h='10' send-count='8'/> <text xml:lang='en' xmlns='urn:ietf:params:xml:ns:xmpp-streams'> You acknowledged 10 stanzas, but I only sent you 8 so far. </text> </stream:error> </stream:stream>
7.流关闭
一个明确的关闭流不同于一个未完成的流。如果客户端希望明确地关闭流并终止会话,它必须发送</stream:stream>,便于服务端发送unavailable presence给客户端。
如果流没有明确地关闭,服务端应该认为流处于未完成的状态(即使客户端关闭了连接服务端的TCP连接)并且应该在有限的时间内维持客户端的会话权力。客户端可以在离开流的未完成状态前发送任何presence。
8.场景
下面的场景演示了流管理的几种不同用法,例子都是有一个客户端和一个服务端,但是流管理同样可以用在服务端和服务端之间的流。
8.1 基本的确认场景
流管理协议可以用来提升使用确认的可靠性,但没有能力恢复会话。一个基础的实现如下:
作为一个客户端,发送无属性的<enable/>,并且忽略响应中<enabled/>的属性。
作为一个服务端,忽略接收到的<enable/>上的属性,并且返回无属性的<enabled>。
当接收到一个<r/>元素,立刻通过一个<a/>元素进行响应,其中'h'值是最后一个处理过的stanza的序列号。
为这个流会话保留一个整数X,初始化为0。当准备发送stanza时,首先把stanza(与当前的X对应)放在未确认队列。然后通过网络发送携带<r/>的stanza,这是为了请求出站stanza的确认信息,并且X加1.当接收到一个携带h属性的<a/>元素,所有小于或等于'h'值的匹配的stanza(此时在队列的X)都要在未确认队列中删除。
这种时间最低程度满足对方,并且允许对每个出站stanza进行基本的追踪。如果流连接中断,应用程序拥有一个未确认stanza队列,它可以适当地选择处理这些stanza。(例如:警告用户或重连后静默发送)
下面的示例演示了基本的确认(在这里客户端自动确认从服务端接收到的每个stanza,而不是先通过<r/>元素提示)
首先,在认证和绑定资源后,客户端启动流管理。
Example 17:客户端启动流管理 <enable xmlns='urn:xmpp:sm:3'/>
服务端启动流管理。
Example 18:服务端启动流管理 <enabled xmlns='urn:xmpp:sm:3'/>
客户端见过名单并马上发送一个<r/>元素请求确认。
Example 19:(客户端发送stanza并且请求确认) <iq id='ls72g593' type='get'> <query xmlns='jabber:iq:roster'/> </iq> <r xmlns='urn:xmpp:sm:3'/>
服务端处理客户端stanza(这里的是返回名单)并且发送一个<a/>元素来确认stanza的处理。
Example 20:服务端处理客户端的stanza并且确认处理了客户端的stanza <iq id='ls72g593' type='result'> <query xmlns='jabber:iq:roster'> <item jid='juliet@capulet.lit'/> <item jid='benvolio@montague.lit'/> </query> </iq> <a xmlns='urn:xmpp:sm:3' h='1'/>
客户端选择确认从服务端接收到的stanza(虽然这里没必要这么做,因为服务端没有请求确认)。发送启动presence,并且马上发送<r/>元素确认请求,内部记录被服务器处理过的stanza数量字段加1。
Example 21:客户端确认处理服务端的第一个stanza,发送stanza并且请求确认。 <a xmlns='urn:xmpp:sm:3' h='1'/> <presence/> <r xmlns='urn:xmpp:sm:3'/>
服务端立即发送一个<a/>来确认对stanza的处理,然后广播用户状态(包括如下所示的用户本身)。
Example 22:服务端确认第二个客户端stanza并且发送stanza <a xmlns='urn:xmpp:sm:3' h='2'/> <presence from='romeo@montague.lit/orchard' to='romeo@montague.lit/orchard'/>
客户端确认服务端的第二个stanza并且发送一个出站消息,后面携带一个<r/>元素。
Example 23:客户端确认服务端的第二个stanza,并且发送一个stanza,随后请求确认 <a xmlns='urn:xmpp:sm:3' h='2'/> <message to='juliet@capulet.lit'> <body>ciao!</body> </message> <r xmlns='urn:xmpp:sm:3'/>
服务端立即发送一个<a/>元素来确认第三个客户端stanza,然后将该stanza路由到远程联系人(这里没有显示是因为服务端没有想客户端发送stanza)
Example 24:(服务端确认第三个客户端stanza) <a xmlns='urn:xmpp:sm:3' h='3'/>
等等。
8.2 高效的确认场景
基本的确认场景是浪费资源的,因为客户端为每个stanza都请求确认。一个更有效的方式是定期请求确认(例如,每5个stanza)。这在下面的伪XML中有显示。
Example 25:(一个高效的会话) <!-- Client --> <enable/> <!-- Server --> <enabled/> <!-- Client --> <message/> <message/> <message/> <message/> <message/> <r/> <!-- Server --> <a h='5'/> <!-- Client--> <message/> <message/> <message/> <message/> <message/> <r/> <!-- Server --> <a h='10'/>
特别是在移动网络,建议只有当实体有其它的数据要发送时才请求并且/或者发送确认,或者通过whitespace keepalive或XMPP ping(XEP-0199)代替。
9.安全考虑
综上所述,服务端必须在客户端通过身份认证后才允许客户端恢复流管理会话,这有助于防止会话劫持。
10.INNA注意事项
本XEP不需要和Internet Assigned Numbers Authority (IANA)交互。
11.XMPP注册注意事项
11.1 协议命名空间
该规范定义了如下的XML命名空间:
urn:xmpp:sm:3
XMPP Registrar 包含了上述命名空间在注册列表中(https://xmpp.org/registrar/namespaces.html),这在XMPP Registrar Function (XEP-0053) 中有定义。
11.2 协议版本控制
如果本规范中定义的协议修订后不完全兼容旧版本,则XMPP注册商应按照XEP-0053 Section 4的描述,增加命名空间末尾的版本号。
11.3 流特性
XMPP注册商在注册表中包含了'urn:xmpp:sm:3'的流特性,在https://xmpp.org/registrar/stream-features.html有定义。
12.XML文档结构
<?xml version='1.0' encoding='UTF-8'?> <xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema' targetNamespace='urn:xmpp:sm:3' xmlns='urn:xmpp:sm:3' elementFormDefault='qualified'> <xs:annotation> <xs:documentation> The protocol documented by this schema is defined in XEP-0198: http://www.xmpp.org/extensions/xep-0198.html </xs:documentation> </xs:annotation> <xs:import namespace='urn:ietf:params:xml:ns:xmpp-stanzas' schemaLocation='http://xmpp.org/schemas/stanzaerror.xsd'/> <xs:element name='a'> <xs:complexType> <xs:simpleContent> <xs:extension base='empty'> <xs:attribute name='h' type='xs:unsignedInt' use='required'/> </xs:extension> </xs:simpleContent> </xs:complexType> </xs:element> <xs:element name='enable'> <xs:complexType> <xs:simpleContent> <xs:extension base='empty'> <xs:attribute name='max' type='xs:positiveInteger' use='optional'/> <xs:attribute name='resume' type='xs:boolean' use='optional' default='false'/> </xs:extension> </xs:simpleContent> </xs:complexType> </xs:element> <xs:element name='enabled'> <xs:complexType> <xs:simpleContent> <xs:extension base='empty'> <xs:attribute name='id' type='xs:string' use='optional'/> <xs:attribute name='location' type='xs:string' use='optional'/> <xs:attribute name='max' type='xs:positiveInteger' use='optional'/> <xs:attribute name='resume' type='xs:boolean' use='optional' default='false'/> </xs:extension> </xs:simpleContent> </xs:complexType> </xs:element> <xs:element name='failed'> <xs:complexType> <xs:sequence xmlns:err='urn:ietf:params:xml:ns:xmpp-stanzas' minOccurs='0' maxOccurs='1'> <xs:group ref='err:stanzaErrorGroup'/> </xs:sequence> <xs:attribute name='h' type='xs:unsignedInt' use='optional'/> </xs:complexType> </xs:element> <xs:element name='r' type='empty'/> <xs:element name='resume' type='resumptionElementType'/> <xs:element name='resumed' type='resumptionElementType'/> <xs:element name='sm'> <xs:complexType> <xs:choice> <xs:element name='optional' type='empty'/> <xs:element name='required' type='empty'/> </xs:choice> </xs:complexType> </xs:element> <xs:complexType name='resumptionElementType'> <xs:simpleContent> <xs:extension base='empty'> <xs:attribute name='h' type='xs:unsignedInt' use='required'/> <xs:attribute name='previd' type='xs:string' use='required'/> </xs:extension> </xs:simpleContent> </xs:complexType> <xs:simpleType name='empty'> <xs:restriction base='xs:string'> <xs:enumeration value=''/> </xs:restriction> </xs:simpleType> <xs:element name='handled-count-too-high'> <xs:complexType> <xs:attribute name='send-count' type='xs:unsignedInt' use='optional'/> <xs:attribute name='h' type='xs:unsignedInt' use='optional'/> </xs:complexType> </xs:element> </xs:schema>