XEP-0060
本文的英文原文来自XEP-0060
XEP-0060: 发布-订阅
摘要: 本文定义了一个XMPP协议扩展来实现实现通用的 发布-订阅 功能。这个协议使 XMPP实体能在一个pubsub服务创建节点(主题)并发布信息到那些节点上;然后一个事件通知(携带或未带载荷)被广播到所有订阅了该节点的实体. PubSub因此坚持了经典的观察者设计模式,并可以作为广泛应用的服务基础,包括新闻提要,内容整合,富出席信息,地理位置,工作流系统,网络管理系统,以及任何其他需要事件通知的应用。
作者: Peter Millard, Peter Saint-Andre, Ralph Meijer
版权: © 1999 - 2013 XMPP标准化基金会(XSF). 参见法律通告.
状态: 草案
类型: 标准跟踪
版本: 1.13
最后更新日期: 2010-07-12
注意: 这里定义的协议是XMPP标准化基金会的一个草案标准.对本协议的执行是被鼓励的,也适于布署到生产系统,但是在它成为最终标准之前可能还会有一些变动.
目录 |
绪论
概览
本文定义的XMPP 发布-订阅 扩展提供了一个框架用于广泛的应用, 包括新闻摘要, 内容整合, 扩展的出席信息, 地理位置, 头像管理, 共享的标签, 拍卖和贸易系统, 工作流系统, 网络管理系统, NNTP网关, 资料管理, 以及任何其他需要事件通知的应用.
这个技术使用了经典的 "发布-订阅" 或曰 "观察者" 设计模式: 一个人或应用发布信息, 同时一个事件通知 (包含或不包含有效载荷) 被广播到所有授权的订阅者. 通常, 发布者和订阅者之间的联系是由一个服务来调节的,这个服务接收发布请求,广播事件通知到订阅者, 并允许特许实体能管理被授权发布或订阅的人员或应用的列表. 对于发布和订阅的焦点是一个节点 "node" ,它是发布者发送数据的目的地,也是订阅者接收通知的目的地. 节点也维护一个事件历史并提供其他服务以补充纯的 pubsub 模式.
本文定义一个通用的协议,所有 pubsub 应用都能使用. 兼容的实现不需要实现这里定义的所有特性 (参见 特性汇总.) 其他协议可以定义 发布-订阅 的子集 "subsets" 或范本 "profiles" 用于特定的场合, 但是这些范本超出了本文的范围.
它如何工作
尽管本协议很大,因为它定义了多方面的用例和可能的错误流程, 但其基本的思路是简单的:
- 一个实体发布信息到一个 发布-订阅 服务上的一个节点.
- pubsub服务推送一个事件通知到所有被授权可以得知该发布信息的实体.
可能最流行的类似 发布-订阅 功能的应用是内容整合, 它常见于和博客,新闻网站,以及其他互联网可用的经常更新的信息相关的 RSS 和 Atom (RFC 4287 1) 种子. 设想一个<hamlet@denmark.lit>的博客发布例子. 当 Hamlet 写下一篇新博文, 他的博客软件发布该文到一个位于<pubsub.shakespeare.lit>的pubsub节点:
例子 1. 发布者发布一篇新博文
<iq type='set' from='hamlet@denmark.lit/blogbot' to='pubsub.shakespeare.lit' id='pub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <publish node='princely_musings'> <item> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </publish> </pubsub> </iq>
所以那就是 发布-订阅 pubsub 的发布 "pub" 部分.
现在 pubsub 服务通知所有订阅者新博文:
例子 2. 服务通知订阅者
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='foo'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> [ ... ENTRY ... ] </item> </items> </event> </message> <message from='pubsub.shakespeare.lit' to='bernardo@denmark.lit' id='bar'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> [ ... ENTRY ... ] </item> </items> </event> </message> <message from='pubsub.shakespeare.lit' to='horatio@denmark.lit' id='baz'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> [ ... ENTRY ... ] </item> </items> </event> </message> <message from='pubsub.shakespeare.lit' to='bard@shakespeare.lit' id='fez'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> [ ... ENTRY ... ] </item> </items> </event> </message>
这里是一个甚至更简单的例子: 一个临时的节点只发送通知而不带有效载荷:
例子 3. 一个临时通知
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='elsinore/doorbell'/> </event> </message>
自然, 涉及的实体为了得到完整的 pubsub 功能可能需要完成其他用例 -- 例如, 发布者可能需要建立一个节点 (见 创建节点) 并且订阅者可能需要为通知报名(见 订阅节点). 这些用例在本文的其余部分有完整的描述. (关于哪些特性是必需的,哪些是推荐的或可选的信息, 参考 特性汇总.)
术语表
以下术语用于本文中涉及的pubsub服务上下文中的元素,对象,或动作.(注意: 本文的一些术语在正文中有更详细的解释.)
- Authorize Access Model 授权访问模式
- 一种节点访问模式,一个实体只能在节点所有者批准了订阅申请之后才能订阅(订阅申请被接受但是只是临时的),并且只有订阅者可以获取项目.
- Address 地址
- Collection Node 集合节点
- 一个节点类型,它包含很多节点 和/或 其他集合,但是不发布条目。集合使得展示节点间更复杂的关系成为可能。集合节点定义于PubSub集合节点 4
- |Entity 实体
- 一个以JID为地址的Jabber实体 (客户端,服务,应用等).
- Event 事件
- 一个节点状态的一次变更
- Instant Node 即时节点
- 一个节点,它的NodeID由pubsub服务自动生成
- Item 条目
- 一个XML片段,它由一个节点发布, 从而生成一个事件
- ItemID 条目ID
- 在一个特定的节点上下文中一个条目的唯一标识符
- Leaf Node 叶子节点
- 一个节点类型,它仅包含已发布的条目. 它不是其他节点的一个容器
- Node 节点
- 一个虚拟的位置,信息可以被发布到那,并且从它那能接收到事件通知 和/或 有效载荷(在其他pubsub系统中,它可能被称为“topic”(主题))
- NodeID 节点ID
- 在一个特定的pubsub服务上下文中一个节点的唯一标识符,节点ID要么由节点创建者提供,要么由pubsub服务生成(如果节点创建者请求一个即时节点)。节点ID可以有语义(例如,在一些系统或在类似PEP的pubsub范本中,节点ID可以是一个 用于相关的载荷的XML命名空间)但是这类含义是可选的.如果一个文档定义了在某个给定的节点ID在XMPP pubsub系统的领域中是独特的, 它必须指定相关载荷的XML命名空间.
- Notification 通知
- 一个发送给订阅者的消息通知他们一个事件
- Outcast 被排斥者
- 一个不被允许订阅或发布到某个节点的实体
- Owner 所有者
- 一个节点的管理者,可能不止一个; 经常(但不是必要的)是节点创建者
- Open Access Model 开放访问模式
- 一种节点访问模式,任何实体可以订阅和获取条目而不需要批准.
- Payload 载荷
- 包含在一个发布请求或一个事件通知的<item/>元素中的XML数据。一个给定的载荷由一个XML命名空间和相关的schema来定义。一个定义某个载荷的格式的文档应该指定该载荷是否只用于具有相同的XML命名空间的节点ID,或是否能被用于任何条目ID。这样一个文档也应该对于被发布载荷的那个节点是否单个节点的最佳配置提出建议.
- Personal Eventing 个人事件
- 发布-订阅的一个简化的子集,用于即时消息和出席信息应用程序场景,每个IM用户的JID是一个虚拟的pubsub服务; 细节请看基于Pubsub的个人事件 5.
- Presence Access Model 出席信息访问模式
- Publisher 发布者
- 一个实体,被允许发布条目到一个节点并且自动订阅到该节点
- Publish-Only 仅发布
- 一个实体,被允许发布条目到一个节点但不被允许接收通知. (当自动化实体需要代表所有者生成通知的时候,这个岗位在没有开放访问模式的上下文中是有用的.)
- Pubsub Service 发布订阅服务
- 一个遵循这里定义的协议的 XMPP 服务器或组件
- Roster Access Model 名册访问模式
- 一种节点访问模式,任何订阅了所有者的出席信息并且在指定的名册组中的实体订阅该节点并从该节点获取条目;这个访问模式主要适用于即时消息系统
- Subscriber 订阅者
- 订阅了某个节点的一个实体
- Whitelist Access Model 白名单访问模式
- 一种节点访问模式,一个实体只有在显式地被所有者允许的情况下才可以订阅和接收条目(从未授权的实体发出的订阅请求会被拒绝).
需求
pubsub服务的需求可能是由终端用户的需要驱动的,也包括其他可能使用这项服务的组件或服务器。首先,一个用Jabber实现的pubsub服务必须提供基本的特性来实现一个纯的 发布-订阅 模式:
- 一个实体必须(MUST)能够发布事件到一个服务,这样所有某个节点的订阅者能接收到事件通知.参见发布条目到节点.
- 一个实体必需能订阅一个节点(或被告知不允许订阅). 见订阅节点.
- 一个实体必须被允许从属于一个节点。被允许的从属关系包括member(成员)、无(none)、outcast(被排斥者)、所有者(owner)、仅发布者(publish-only)、以及发布者(publisher)。实现必须支持无和所有者的从属关系,可以支持成员、被排斥者、发布者、和仅发布者的从属关系. 见从属关系.
- 一个实体必须被允许查询一个pubsub服务(或一个指定的节点)以确定该服务(或节点)实现提供了本文定义的哪些可选的特性。这个查询必须使用服务查询(disco#info)协议. 见查询节点信息.
一些基于Jabber的pubsub服务可能需要使用其他特性,但是这些特性是可选的(OPTIONAL)所以不是强制要求和本协议兼容的。无论如何,如果以下这些特性被实现了,它们必须(MUST)按照此处本协议的规定来保证兼容性。这些特性包括:
- 一个服务可以对一个节点缓存最近一次发布的条目(即使"persistent-items"选项未被设置成true);如果它确实把缺省的 "cache-last-item" 设置成 true,它应该(SHOULD)按照"send_last_published_item"参数的配置发送最近发布的条目(或关于它的通知)给订阅的实体。
- 一个节点所有者应该能指定谁可以订阅该节点.
- 一个节点所有者应该能指定谁可以发布项目到该节点.
- 一个节点可以被配置成在事件通知内递送已发布的有效载荷.
- 一个节点可以被配置成持久化已发布条目到一些持久存储机构.
- 一个节点可以被配置成只持久化有限次数的已发布条目.
- 一个服务可以支持集合.如XEP-0248所述.
- 一个服务或节点可以支持扩展的服务查询信息(meta-data).
准备工作
从属关系
为了管理许可,此处定义的协议使用了一个层次从属关系,类似多用户聊天 7中所采用的.
所有的从属关系必需基于一个纯JID (<localpart@domain.tld> 或 <domain.tld>) 而不是全JID (<localpart@domain.tld/resource> 或 <domain.tld/resource>).
对于 "owner" 和 "none" 的从属关系的支持是必需的。对于所有其他的从属关系的支持是推荐的(RECOMMENDED)。对于由一个实现支持的每个非必需的从属关系, 它应该返回一个服务查询特性 "name-affiliation" , 这里 "name" 是从属关系的名称, 类似 "member", "outcast", 或 "publisher" (见特性总结). 特殊种类的pubsub服务可以强制额外的需求(例如, 要求支持给定的非必需从属关系或所有从属关系).
表 1: 从属关系和他们的权限
从属关系 | 订阅 | 获取条目 | 发布条目 | 删除单个条目 | 清理节点 | 配置节点 | 删除节点 |
---|---|---|---|---|---|---|---|
所有者 | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
发布者 | Yes | Yes | Yes | Yes * | Yes * | No | No |
仅发布 | No | No | Yes | Yes * | No * | No | No |
成员 | Yes | Yes | No | No | No | No | No |
无 | Yes | No | No | No | No | No | No |
被排斥者 | No | No | No | No | No | No | No |
- 注意: 一旦一个发布者已经发布条目到了那个节点, 服务可以允许任何发布者删除/清理任何条目而不只是允许原始的发布者移除它。对于 仅发布 从属关系,这种行为是不推荐的,该 仅发布 实体将仅被允许删除它曾经发布的条目。
实体变更它和节点的从属关系的方法是很明确的。典型来讲,一个从属关系状态的改变需要所有者的动作。从属关系变更和它们的触发动作定义在下表中。
表 2: 从属关系状态图
被排斥者 | -- | 所有者移除禁令 | 所有者添加实体到成员列表 | 所有者添加实体到发布者列表 | 所有者添加实体到所有者列表 |
无 | 所有者禁止实体 | -- | 所有者添加实体到成员列表 | 所有者添加实体到发布者列表 | 所有者添加实体到所有者列表 |
成员 | 所有者禁止实体 | 所有者从成员列表中移除实体 | -- | 所有者添加实体到发布者列表 | 所有者添加实体到所有者列表 |
发布者 | 所有者禁止实体 | 所有者从发布者列表中移除实体 | n/a | -- | 所有者添加实体到所有者列表 |
所有者 | n/a | 所有者辞职 | n/a | n/a | -- |
订阅状态
订阅某个节点可以有很多状态
表 3: 订阅状态
订阅状态 | 描述 |
---|---|
无 | 节点不能(MUST NOT)发送事件通知或有效载荷给实体. |
未决的 | 一个实体已经申请订阅一个节点,但是这个申请还没被节点所有者批准。处于这个状态时节点不能(MUST NOT)发送事件通知或有效载荷给实体。 |
未配置的 | 一个实体已经订阅但是它的订阅选项还没有配置。在这个状态时节点可以发送事件通知或有效载荷给实体。 服务可以(MAY)让未配置的订阅超时(作废)。 |
已订阅的 | 一个实体已经订阅了一个节点。在这个状态下节点必须发送所有事件通知(和有效载荷,如果配置了的话)给实体(按照订阅者配置和内容过滤)。 |
事件类型
发布-订阅协议需要两个主要的尺度来使我们能够衡量事件:持久性和临时性,纯的通知和包含有效载荷的通知。实现应该让所有者能够从两个尺度都可以配置一个节点。
无论一个节点是否配置用于持久性或者临时性的事件,一个服务可以缓存最后发布到节点的条目,在这种情况下,它应该根据"send_last_published_item"选项(参见本文的 条目缓存)的配置发送那个条目给订阅者; 如果该服务支持 "http://jabber.org/protocol/pubsub#last-published" 特性,那么这个选项的值的缺省值必须是 "on_sub_and_presence" (尽管该服务应该允许该节点所有者覆盖该缺省值).
注意: "on_sub_and_presence" 设置和订阅者的出席信息相关, 而不是和发布者的出席信息相关.
一个pubsub服务必须确认发布请求在这两个方面都满足节点的配置。(参见本文的 发布条目到节点 章节了解相关的出错条件)。
节点配置和期望的事件类型决定了是否一个条目必须由发布者提供,该条目是否在发布请求或事件通知中包含一个载荷,以及一个条目的I是由发布者提供还是由pubsub服务生成。我们在下表中总结相关的规则:
表 4: 条目,载荷,以及条目 ID
持久性节点 ** | 发布者必须包含一个 <item/> 元素,它可以是空的或者包含一个载荷;即使发布请求包含一个载荷,pubsub服务也不能(MUST NOT)在事件通知中包含载荷;如果发布请求不包含条目ID,pubsub服务必须生成条目ID | 发布请求必须包含一个 <item/> 元素,其中应该包含一个载荷;如果发布请求包含一个载荷,事件通知必需包含该载荷;如果发布请求不包含条目ID,pubsub服务必须生成条目ID |
临时性节点 ** | 发布请求不能(MUST NOT)包含一个 <item/> 元素;载荷不被包含在发布请求或事件通知中,尽管事件通知必须包含一个空的<items/>元素;条目ID既不会由发布请求提供也不会由pubsub服务生成 | 发布请求必须包含一个 <item/> 元素,其中包含一个载荷;如果发布请求包含一个载荷,事件通知必需包含该载荷;pubsub服务可以生成一个条目ID |
- 注意: 节点通知是仅通知的还是包含载荷, 由 "pubsub#deliver_payloads" 配置项来决定.
- 注意: 该节点是持久的还是临时的,由 "pubsub#persist_items" 配置项来决定.
节点类型
有两种节点类型:
表 5: 节点类型
节点类型 | 描述 |
---|---|
叶节点 | 一个仅包含发布的条目的节点。它不包含任何其他节点。这是最常见的节点类型。 |
集合节点 | 一个包含节点和/或其他集合但是不包含发布项目的节点。集合使得节点之间更复杂的关系成为可能。详见 XEP-0248. |
节点访问模式
为了使节点创建对客户端更加简单,我们定义了以下节点访问模式(为了公开性):
表 6: 节点访问模式
访问模式 | 描述 |
---|---|
开放的 | 任何实体可以订阅这个节点(即, 订阅不需要批准) ,并且任何实体可以从这个节点获取条目(即,不需要被订阅);这应该是一般pubsub服务的缺省访问模式。 |
出席信息 | 任何拥有"from"或"both"类型的订阅的实体可以向节点订阅并获取条目;这个访问模式主要应用于即时消息系统 (参见 RFC 3921 ). |
名册 | 任何处于指定名册组中的实体可以向节点订阅和获取条目;这种访问模式主要用于即时消息系统 (参见 RFC 3921 ). |
授权的 | 节点所有者必须批准所有订阅请求,并且只有订阅者可以从节点获取条目. |
白名单 | 一个实体仅仅在被节点所有者加入白名单的时候才可以订阅和获取条目。节点所有者必须自动进入白名单。为了添加实体到白名单,节点所有者应该y使用本文的 管理从属关系 中定义的协议,特别是把从属关系设为"member". |
一个通用的发布-订阅实现应该支持所有已定义的访问模式, 尽管专门的 发布-订阅 实现可以只支持访问模式的一个子集. 在一个特定的部署中提供哪个访问模式是一个服务供应的问题(比如,一些受限的部署可能希望锁定许可从而只提供“授权的”和"白名单"的访问模式,或甚至只提供"白名单"的访问模式).
一个节点创建者或所有者可以覆盖缺省的访问模式,通过给'pubsub#access_model'配置项指定一个适当的值(参见本文的 以缺省值创建节点 和 配置节点).
寻址
如果一个 pubsub 节点是可设定地址的,它必须(MUST)被设定为一个JID或一个JID和一个节点的组合。7
JID
如果一个 pubsub 节点的地址被设定成一个JID,节点ID必须(MUST)是资源标识符,并且节点ID不能(MUST NOT)是JID中的"user"(节点标识符)部分(如"domain.tld/NodeID" 和 "user@domain.tld/NodeID" 是被允许的; "NodeID@domain.tld" 是不允许的9)。JID 寻址应该在使用一个不支持节点属性的协议和一个 pubsub 节点交互的时候使用. 例如,当一个服务允许实体通过出席信息订阅节点, 它会把节点地址设成JID。如果一个 pubsub 节点可以被设置成 JID ,pubsub 服务必须确保节点ID符合 RFC 3920 所述的Stringprep中的Resourceprep范本。
考虑以下例子,pubsub服务定位于主机 pubsub.shakespeare.lit.
例子 4. 节点地址设定为 domain.tld/NodeID
<iq to='pubsub.shakespeare.lit/news announcements'> ... </iq>
现在再考虑以下例子, pubsub 服务定位于 pubsub@shakespeare.lit.
例子 5. 节点地址设定为 user@domain.tld/NodeID
<iq to='pubsub@shakespeare.lit/news announcements'> ... </iq>
JID+NodeID
如果一个pubsub节点地址可以被设成一个JID加节点,这个节点ID必须既是服务查询的'node'属性又是pubsub服务的'node'属性;即,用于查询时,一个pubsub节点等同于一个服务查询节点。如果一个pubsub节点可以被设置成 JID 加 node,这个pubsub服务应该节点ID符合 RFC 3920 所述的Stringprep中的Resourceprep范本。
考虑以下例子,(虚拟的)pubsub服务位于 hamlet@denmark.lit.
例子 6. 节点地址设为 JID+NodeID
<iq to='hamlet@denmark.lit'> <query node='princely_musings'/> </iq>
实体用例
本章定义了使用案例,用于任何希望和一个发布-订阅服务交互的实体使用的任何协议,主要集中于服务查询的用例。
查询特性
一个服务必须回应由'http://jabber.org/protocol/disco#info'命名空间限定的服务查询信息请求。由一个pubsub服务返回的"disco#info"结果必须表明服务的标识符以及支持哪些pubsub特性。
案例子 7. 实体查询 Pubsub 服务支持的特性
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='feature1'> <query xmlns='http://jabber.org/protocol/disco#info'/> </iq>
例子 8. Pubsub 服务返回支持的特性组
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='feature1'> <query xmlns='http://jabber.org/protocol/disco#info'> <identity category='pubsub' type='service'/> <feature var='http://jabber.org/protocol/pubsub'/> </query> </iq>
可能的 pubsub 特性在本文中到处都是,并且它们都已经被注册了,如本文的 XMPP注册处事项 所述。想了解哪个特性是必需的,建议的,可选的,参见本文的 特性总结 。
查询节点
如果一个服务实现了一个多层次节点(也就是 集合节点),它必须(MUST)也要让实体能够通过 服务查询 协议在这些层级中查询节点,在结果集很大的时候遵照 XEP-0030 的建议(这时候应该使用Jabber search] 10 或一些其他协议).以下例子展示在一个多层次 pubsub 服务中如何使用 服务查询 来查询可用的节点.
注意: 节点层次和集合节点是可选的. 详细情况参照本文的 节点ID语义 和 集合节点.
在第一个例子中,一个实体向一个根节点(即服务本身)发送一个服务查询条目("disco#items")请求,它是一个集合节点:
例子 9. 实体请求所有一级节点
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='nodes1'> <query xmlns='http://jabber.org/protocol/disco#items'/> </iq>
例子 10. 服务返回所有一级节点
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='nodes1'> <query xmlns='http://jabber.org/protocol/disco#items'> <item jid='pubsub.shakespeare.lit' node='blogs' name='Weblog updates'/> <item jid='pubsub.shakespeare.lit' node='news' name='News and announcements'/> </query> </iq>
第二个例子中,一个实体发送一个 disco#items 请求给其中一个一级节点,它也是一个集合节点:
'例子 11. 实体请求二级节点
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='nodes2'> <query xmlns='http://jabber.org/protocol/disco#items' node='blogs'/> </iq>
例子 12. 服务返回二级节点
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='nodes2'> <query xmlns='http://jabber.org/protocol/disco#items' node='blogs'> <item jid='pubsub.shakespeare.lit' node='princely_musings'/> <item jid='pubsub.shakespeare.lit' node='kingly_ravings'/> <item jid='pubsub.shakespeare.lit' node='starcrossed_stories'/> <item jid='pubsub.shakespeare.lit' node='moorish_meanderings'/> </query> </iq>
如果一个节点是叶子节点而不是一个集合节点并且有条目被发布到该节点,这个服务可以为每一个已发布的条目返回一个 <item/> 元素,如本文的查询节点条目所述,无论如何这类条目不能(MUST NOT)包含一个'node'属性(因为它们是已发布的条目,不是节点).
查询节点节点信息
一个 pubsub 服务必须允许实体去查询每个单独的节点来获得该节点的相关信息。必须(MUST)使用服务查询协议来查询这些信息。"disco#info" 结果必须包含一个标识符,其类别是“pubsub”,类型是“leaf”或“collection”。
注意: 如果一个节点的标识符的类型为“leaf”,它不能(MUST NOT)包含其它的节点或集合(只有条目);如果一个节点的ID类型是“collection”,它不能(MUST NOT)包含条目(只有其它节点或集合)。
'例子 13. 实体查询集合节点的信息
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='info2'> <query xmlns='http://jabber.org/protocol/disco#info' node='blogs'/> </iq>
例子 14. 服务应答pubsub/collection标识符
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='meta1'> <query xmlns='http://jabber.org/protocol/disco#info' node='blogs'> <identity category='pubsub' type='collection'/> </query> </iq>
例子 15. 实体查询叶子节点信息
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='info1'> <query xmlns='http://jabber.org/protocol/disco#info' node='princely_musings'/> </iq>
例子 16. 服务应答 pubsub/leaf 标识符
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='info1'> <query xmlns='http://jabber.org/protocol/disco#info' node='princely_musings'> ... <identity category='pubsub' type='leaf'/> ... </query> </iq>
查询节点元数据
"disco#info" 结果可以包含关于节点的详细元数据,封装在服务发现扩展|服务发现扩展 12 定义的数据表格 11 格式中,这里的数据表格上下文通过包含一个 "http://jabber.org/protocol/pubsub#meta-data" 中定义的 FORM_TYPE 来指定的,并遵循 数据表格的字段标准化 13。如果元数据被提供了,它应该包含所有已配置的选项值,像"automatic" 信息一样,比如节点创建日期,发布者列表以及类似的信息。
例子 17. 实体查询节点的信息
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='meta1'> <query xmlns='http://jabber.org/protocol/disco#info' node='princely_musings'/> </iq>
例子 18. 服务应答信息和元数据
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='meta1'> <query xmlns='http://jabber.org/protocol/disco#info' node='princely_musings'> <identity category='pubsub' type='leaf'/> <feature var='http://jabber.org/protocol/pubsub'/> <x xmlns='jabber:x:data' type='result'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#meta-data</value> </field> <field var='pubsub#type' label='Payload type' type='text-single'> <value>http://www.w3.org/2005/Atom</value> </field> <field var='pubsub#creator' label='Node creator' type='jid-single'> <value>hamlet@denmark.lit</value> </field> <field var='pubsub#creation_date' label='Creation date' type='text-single'> <value>2003-07-29T22:56Z</value> </field> <field var='pubsub#title' label='A short name for the node' type='text-single'> <value>Princely Musings (Atom)</value> </field> <field var='pubsub#description' label='A description of the node' type='text-single'> <value>Updates for Hamlet's Princely Musings weblog.</value> </field> <field var='pubsub#language' label='Default language' type='list-single'> <value>en</value> </field> <field var='pubsub#contact' label='People to contact with questions' type='jid-multi'> <value>bard@shakespeare.lit</value> </field> <field var='pubsub#owner' label='Node owners' type='jid-multi'> <value>hamlet@denmark.lit</value> </field> <field var='pubsub#publisher' label='Publishers to this node' type='jid-multi'> <value>hamlet@denmark.lit</value> </field> <field var='pubsub#num_subscribers' label='Number of subscribers to this node' type='text-single'> <value>1066</value> </field> </x> </query> </iq>
注意: 节点元数据可以用多种方法设置。一些是基于节点配置(如所有者的JID),也有的是动态的(如订阅者的编号)。任何在节点元数据中提供的静态信息应该(SHOULD)在节点配置表单中以字段形式提供。
注意: pubsub#language 字段应该是单列的,这样pubsub服务能展示一个适当的语言和语言代码列表.
查询节点条目
为了查询服务中特定节点的已发布条目,一个实体可以发送"disco#items"请求给节点本身,服务可以通过服务查询的<item/>元素返回每个条目。每个服务查询条目的'name'属性必须包含它的ItemID,并且该条目不能(MUST NOT)有'node'属性。这个ItemID可以用于获取条目,使用本文中从节点获取条目定义的协议.
'例子 19. 实体请求一个节点的所有条目
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='items1'> <query xmlns='http://jabber.org/protocol/disco#items' node='princely_musings'/> </iq> <iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <query xmlns='http://jabber.org/protocol/disco#items' node='princely_musings'> <item jid='pubsub.shakespeare.lit' name='368866411b877c30064a5f62b917cffe'/> <item jid='pubsub.shakespeare.lit' name='3300659945416e274474e469a1f0154c'/> <item jid='pubsub.shakespeare.lit' name='4e30f35051b7b8b42abe083742187228'/> <item jid='pubsub.shakespeare.lit' name='ae890ac52d0df67ed7cfdf51b644e901'/> </query> </iq>
找回订阅
一个实体可能想查询服务以找回它对服务中所有节点的订阅。推荐支持这个特性("retrieve-subscriptions").
为了做出这些查询,请求的实体必须发送一个 IQ-get 节,这个节的 <pubsub/> 子元素包含一个没有属性的空<subscriptions/>元素。
例子 20. 实体请求所有当前的订阅
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='subscriptions1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscriptions/> </pubsub> </iq>
如果该服务返回一个订阅列表,它必须(MUST)返回所有和请求中'from'属性中的纯JID(<localpart@domain.tld> or <domain.tld>)部分匹配的JIDs的订阅.
对于每个订阅, 返回一个 <subscription/> 元素以指明 NodeID, 以及这个节点ID所从属的JID(可以包含一个资源,取决于实体如何订阅的),以及当前的订阅状态。如果服务支持订阅标识符,'subid' 属性也必须出现.
例子 21. 服务返回所有当前订阅
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='subscriptions1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscriptions> <subscription node='node1' jid='francisco@denmark.lit' subscription='subscribed'/> <subscription node='node2' jid='francisco@denmark.lit' subscription='subscribed'/> <subscription node='node5' jid='francisco@denmark.lit' subscription='unconfigured'/> <subscription node='node6' jid='francisco@denmark.lit' subscription='subscribed' subid='123-abc'/> <subscription node='node6' jid='francisco@denmark.lit' subscription='subscribed' subid='004-yyy'/> </subscriptions> </pubsub> </iq>
如果请求实体没有订阅,pubsub 服务必须返回一个空的 <subscriptions/> 元素.
例子 22. 没有订阅
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='subscriptions1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscriptions/> </pubsub> </iq>
如果服务不支持订阅找回,该服务必须应答一个<feature-not-implemented/> 错误, 指定一个pubsub特有的错误条件 <unsupported/> 以及特性 "retrieve-subscriptions".
例子 23. 不支持订阅找回
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='subscriptions1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='retrieve-subscriptions'/> </error> </iq>
一个实体也可以在一个特定节点请求它的所有订阅(例如, 如果它的订阅有多个SubIDs(子ID)),通过在<subscriptions/>元素包含一个'node'属性.
例子 24. 实体从一个特定节点请求当前所有订阅
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='subscriptions2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscriptions node='princely_musings'/> </pubsub> </iq>
服务以后将只返回该实体的订阅到那个特定的节点; 这就像在该实体的订阅上做了个过滤器.
例子 25. 服务返回所有当前订阅到一个特定节点
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='subscriptions2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscriptions node='node6'> <subscription node='node6' jid='francisco@denmark.lit' subscription='subscribed' subid='123-abc'/> <subscription node='node6' jid='francisco@denmark.lit' subscription='subscribed' subid='004-yyy'/> </subscriptions> </pubsub> </iq>
找回从属关系
一个实体可能希望查询服务以找回它在该服务上的所有节点的从属关系,或查询一个特定的节点以找回在那个节点的从属关系。推荐支持这个特性("retrieve-affiliations").
为了向服务做出这些请求,请求实体包含一个没有属性的空 <affiliations/> 元素。
例子 26. 实体请求所有当前从属关系
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='affil1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <affiliations/> </pubsub> </iq>
如果服务返回一个从属关系列表,它必须返回所有和该请求的'from'属性的纯JID(<localpart@domain.tld> or <domain.tld>)部分相匹配的JID的从属关系。
对于每个从属关系, 返回一个 <affiliation/> 元素,包含 NodeID 和从属关系状态(所有者, 发布者, 仅发布,成员,或 被排斥者).
例子 27. 服务应答所有当前从属关系
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='affil1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <affiliations> <affiliation node='node1' affiliation='owner'/> <affiliation node='node2' affiliation='publisher'/> <affiliation node='node5' affiliation='outcast'/> <affiliation node='node6' affiliation='owner'/> </affiliations> </pubsub> </iq>
如果请求实体没有从属关系, pubsub服务必须返回一个空的 <affiliations/> 元素.
例子 28. 没有从属关系
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='affil1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <affiliations/> </pubsub> </iq>
如果该服务不支持从属关系找回, 服务必须应答一个 <feature-not-implemented/> 错误, 指明一个pubsub特有的错误条件 <unsupported/> 和 一个 "retrieve-affiliations" 的特性.
例子 29. 不支持从属关系找回
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='affil1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='retrieve-affiliations'/> </error> </iq>
为了执行一个对特定节点的从属关系请求, 请求实体包含一个带'node'属性的空<affiliations/>元素.
例子 30. 实体请求特定节点的从属关系
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='affil2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <affiliations node='node6'/> </pubsub> </iq>
例子 31. 服务返回当前从属关系
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit' id='affil2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <affiliations> <affiliation node='node6' affiliation='owner'/> </affiliations> </pubsub> </iq>
订阅者用例
本节定义了潜在和实际的订阅者使用协议的用例。(注意:本文的实现备注描述了一个pubsub服务必须遵守的许多重要的因素和商业规则.另外,所有例子都假定独立的pubsub组件存在并包含任何服务器或网络标记的相关'from'地址).
订阅节点
请求
当一个Jabber实体希望订阅一个节点,它向pubsub服务发送订阅请求。订阅请求是一个 IQ-set 消息,其<pubsub/> 元素包含并且仅包含一个<subscribe/>元素.这个<subscribe/>元素应该拥有一个'node' 属性指明实体希望订阅的节点.这个<subscribe/>元素也必须拥有一个'jid' 属性指明用于订阅的JID确切的XMPP地址--通常是一个纯JID(<localpart@domain.tld> 或 <domain.tld>) 或全JID(<localpart@domain.tld/resource> or <domain.tld/resource>).
这里是一个订阅请求的例子.
例子 32. 实体订阅节点
<iq type='set' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscribe node='princely_musings' jid='francisco@denmark.lit'/> </pubsub> </iq>
成功场景
如果订阅请求被成功地处理, 该服务器必须通知该请求实体它已经被订阅了(它可以包含一个服务器生成的SubID).
例子 33. 服务返回成功
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscription node='princely_musings' jid='francisco@denmark.lit' subid='ba49252aaa4f5d320c24d3766f0bdcade78c78d3' subscription='subscribed'/> </pubsub> </iq>
错误场景
有很多原因可能导致订阅请求失败:
- JIDs的纯JID部分不匹配.
- 节点有一个"presence"访问模式并且请求实体没有订阅所有者的出席信息。
- 节点有一个"roster"的访问模式并且请求实体不在授权名册组中。
- 节点有一个"whitelist"的访问模式并且请求实体不在白名单中。
- 订阅节点的时候服务需要付费。
- 请求实体是匿名的并且服务不允许匿名实体订阅。
- 请求实体有一个未决的订阅。
- 请求实体被禁止订阅(例如,因为从属关系是被排斥者)。
- 请求实体尝试建立太多订阅.
- 节点不支持订阅。
- 节点已经被移走了。
- 节点不存在。
更完整的错误案例在以下章节.
JIDs不匹配
如果指定的JID是一个纯JID或全JID, 该服务必需最低程度检查纯JID部分和接收到的IQ请求的'from'属性的纯JID部分以确保请求实体和正在请求加入订阅者列表的JID是同一个实体.
如果JIDs的纯JID部分不符合以上描述并且请求实体没有被实现定义某种管理或代理特权, 该服务必须返回一个<bad-request/>错误, 它应该也包含一个pubsub特有的错误条件<invalid-jid/>.
例子 34. JIDs不匹配
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <invalid-jid xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
注意: 实现可以允许该服务的管理员配置一个实体列表免于这种检查; 那些实体可以被认为是"可信的代理",被允许代表其他实体订阅. 同样的办法, 实现可以允许实体黑名单,不被允许执行特定的操作(类似订阅或创建节点).
要求订阅出席信息
对于拥有"presence"访问模式的节点,如果请求实体没有订阅所有者的出席信息,那么pubsub服务必须返回一个 <not-authorized/> 错误,它也应该包含一个pubsub特有的<presence-subscription-required/>错误条件。
例子 35. 实体没有被授权创建一个订阅(需要出席信息订阅)
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='auth'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <presence-subscription-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
不在名册组中
对于拥有"roster"访问模式的节点,如果请求实体不在授权的名册组中,pubsub服务必须返回一个 <not-authorized/> 错误,它也应该也包含一个 pubsub特有的<not-in-roster-group/>错误条件。
例子 36. 实体没有被授权创建一个订阅(不在名册组中)
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='auth'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <not-in-roster-group xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
不在白名单中
对于拥有"白名单"节点访问模式的节点,如果请求实体不在白名单中,pubsub服务必须返回一个 <not-allowed/> 错误,指定一个pubsub特有的<closed-node/>错误条件。
例子 37. 节点有白名单访问模式
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='cancel'> <not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <closed-node xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
要求付款
商业部署可能希望把订阅者连接到一个付费客户数据库。如果订阅者需要付费才能订阅该节点(例如,如果订阅者不在客户数据库中或客户还未付帐),该服务应该返回一个 <payment-required/> 错误给订阅者。
例子 38. 订阅需要付费
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='auth'> <payment-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
不允许匿名订阅
一些 XMPP 服务器可能允许使用SASL ANONYMOUS验证;无论如何,因为这会导致实体不稳定(分配的JID可能不是同一个负责人持续拥有的),一个服务可以阻止匿名实体订阅一个节点并且应该使用服务查询来决定是否有一个实体的标识符是 "account/anonymous".如果一个请求实体是匿名的但是服务器不允许匿名实体订阅,该服务应该返回一个< forbidden/>错误给订阅者.
例子 39. 请求实体是匿名用户
<iq type='error' from='pubsub.shakespeare.lit' to='anonymous@denmark.lit/foo' id='sub1'> <error type='cancel'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
订阅未决
如果请求实体有一个未决的订阅, 服务必须返回一个<not-authorized/>错误给订阅者,表明一个发生了pubsub特有的<pending-subscription/>错误条件.
例子 40. 请求实体有一个未决的订阅
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='auth'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <pending-subscription xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
被屏蔽
如果请求实体被屏蔽订阅(例如, 因为它有一个被排斥者的从属关系), 服务器必须返回一个<forbidden/>错误给订阅者.
例子 41. 请求实体被屏蔽
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
太多订阅
如果请求实体尝试建立太多订阅("太多"的定义是本地服务策略的问题), 该服务必须返回一个<policy-violation/>错误给订阅者, 指定一个pubsub特有的错误条件<too-many-subscriptions/>.
例子 42. 请求实体超出了订阅数量限制
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='wait'> <policy-violation xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <too-many-subscriptions xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
该服务可能匹配订阅实体的纯JID或全JID来确定是否一个实体请求了太多订阅.
不支持订阅
如果节点不允许实体订阅, 服务应该返回一个<feature-not-implemented/>错误给订阅者,指定一个pubsub特有的<unsupported/>错误条件和一个"subscribe"特性.
例子 43. 订阅不支持
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='subscribe'/> </error> </iq>
节点被移走了
如果该节点被移走了, 该服务应该返回一个<gone/>错误(如果该节点被永久性的移走了)或一个<redirect/>错误(如果该节点被临时移走了).
例子 44. 节点被移走了
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='modify'> <gone xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'> xmpp:pubsub.shakespeare.lit?;node=some-other-node </gone> </error> </iq>
节点不存在
如果节点不存在, 服务应该返回一个<item-not-found/>错误给订阅者.
例子 45. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
要求批准
对于拥有"authorize"访问模式的节点, 订阅请求必须由节点所有者批准, 除非服务策略允许非"none"实体自动订阅(例如,成员和发布者可能被允许自动订阅); 所以pubsub服务发送一个消息给节点所有者请求授权(参见本文的 管理订阅请求). 因为订阅请求可能被批准也可能不被批准, 服务必须返回一个未决通知给订阅者.
例子 46. 服务应答未决
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscription node='princely_musings' jid='francisco@denmark.lit' subscription='pending'/> </pubsub> </iq>
要求配置
如果实体在接收事件通知之前必须配置它的订阅选项(见本文 配置订阅选项),服务必须通知实体这件事. 它应该返回一个IQ-result给请求实体一个记号表示需要订阅配置。
例子 47. 服务应答成功并指示要求订阅配置
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscription node='princely_musings' jid='francisco@denmark.lit' subscription='unconfigured'> <subscribe-options> <required/> </subscribe-options> </subscription> </pubsub> </iq>
注意: 只有在订阅者必须在收到任何事件通知之前配置订阅,节点才将包含这个<required/>子元素. 如果配置是必需的而配置请求没有在合理的时间内提交,一个服务可以判定订阅请求超时(取决于服务或节点的配置).
替代的方案是, 如果没有同步做配置就不能创建这个订阅, 服务可以返回一个<not-acceptable/> 错误, 指定一个pubsub特有的<configuration-required/>错误条件.
例子 48. 服务返回错误指出要求订阅配置
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscribe node='princely_musings' jid='francisco@denmark.lit'/> <options node='princely_musings' jid='francisco@denmark.lit'> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> <field var='pubsub#deliver'><value>1</value></field> <field var='pubsub#digest'><value>0</value></field> <field var='pubsub#include_body'><value>false</value></field> <field var='pubsub#show-values'> <value>chat</value> <value>online</value> <value>away</value> </field> </x> </options> </pubsub> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <configuration-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
如果没有包含<required/>元素也没有错误返回, 订阅立刻生效并且实体可以在任何时间配置这个订阅(服务可以通过在IQ-result中包含一个空的<subscribe- options/>元素指出支持订阅选项, 如下案例所示).
例子 49. 服务应答成功并指示订阅选项被支持但不是必需的
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscription node='princely_musings' jid='francisco@denmark.lit' subscription='unconfigured'> <subscribe-options/> </subscription> </pubsub> </iq>
多重订阅
一个实体可能希望使用不同的订阅选项来订阅, 这样它能多次订阅同一个节点. 对这个特性("multi-subscribe")的支持是可选的.
如果允许相同的JID的多重订阅, 该服务必需使用'subid'属性来区分相同实体的多个订阅(所以对于每个node+JID组合来说SubID必须是唯一的并且SubID被发送给订阅者的任何时候都是存在的). 不推荐客户端生成SubIDs, 因为可能导致冲突; 所以一个服务应该代表该订阅者生成SubID并且如果订阅者已经提供了SubID服务可以覆盖它. 如果该服务不允许相同实体多重订阅并且它接收到了额外的订阅请求, 该服务必须返回当前的订阅状态(好像订阅刚被批准).
当pubsub服务生成事件通知, 它应该只发送一个事件通知给一个有多重订阅的实体, 而不是为每个订阅发送一个事件通知. 这里"实体"意味着指定订阅的JID, 要么是纯JID要么是全JID; 无论如何, 如果相同的纯JID有多个订阅但是那些订阅的全JIDs不同(例如, 一个订阅是用于 user@domain.tld./foo 而另一个订阅是用于 user@domain.tld/bar), 该服务必须把它们视为不同的 JIDs 用来生成事件通知.
获取最近发布的条目
当一个订阅请求被成功处理, 服务可以发送最近发布的条目给新订阅者. 包含这个条目的消息应该被标为由'urn:xmpp:delay'命名空间限定的扩展信息(见延迟递送 14) 以表明它是被延迟递送的. (注意在本例子中该事件通知被发送到纯JID,因为它就是订阅JID.)
例子 50. 服务发送最近发布的条目
<message from='pubsub.shakespeare.lit' to='francisco@denmark.lit'> <event xmlns='http://jabber.org/protocol/pubsub#event'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </items> </event> <delay xmlns='urn:xmpp:delay' stamp='2003-12-13T23:58:37Z'/> </message>
如果该服务缺省的配置是对所有节点都发送最近发布的条目(根据节点配置覆盖的), 它必须在它对 disco#info 请求的应答中返回一个特性 "http://jabber.org/protocol/pubsub#last-published" .
从节点取消订阅
请求
为了从一个节点取消订阅, 订阅者发送一个 IQ-set, 它的 <pubsub/> 子元素包含一个<unsubscribe/>元素,指明节点和已订阅的 JID.
例子 51. 实体从一个节点取消订阅
<iq type='set' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='unsub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <unsubscribe node='princely_musings' jid='francisco@denmark.lit'/> </pubsub> </iq>
成功场景
如果请求可以被成功处理,服务必须返回一个 IQ result.
例子 52. 服务应答成功
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'/>
错误场景
有很多原因可能导致取消订阅失败:
- 请求实体对该节点有多个订阅但是没有指定一个订阅ID.
- 请求未指定一个已存在的订阅者
- 请求实体没有足够的权限取消指定JID的订阅.
- 节点不存在.
- 请求指定的订阅ID不合法或不是当前的.
错误场景的更完整的描述如下.
无订阅ID
如果请求实体有多个订阅指向该节点但是没有指定一个订阅ID, 服务必须返回一个<bad-request/>错误, 它也应该也包含一个 <subid-required/>的pubsub特有的错误条件.
例子 53. 实体未指定SubID
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <subid-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
无该订阅者
如果'jid'属性的值未指定一个已存在的订阅者, pubsub服务必须返回一个错误节, 这个节应该是<unexpected-request/>并且也应该包含一个 <not-subscribed/>的pubsub特有的错误条件.
例子 54. 请求实体不是一个订阅者
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'> <error type='cancel'> <unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
权限不足
如果请求实体被禁止取消指定JID的订阅, 服务必须返回一个<forbidden/>错误. 服务必须检查这个做出请求的实体是否被授权可以取消订阅. 如果订阅者的JID格式是<localpart@domain.tld/resource> 或 <domain.tld/resource>, 服务必须通过比较两个JID的<localpart@domain.tld> 或 <domain.tld> 部分来执行这个检查以确保它们是匹配的. 如果这些JID的纯JID部分不匹配并且请求实体没有被授权取消这个JID的订阅(例如, 因为它不是一个服务管理员或被授权的代理), 服务必须返回一个<forbidden/>错误.
例子 55. 请求实体被禁止取消订阅实体
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
节点不存在
如果节点不存在, pubsub服务必须返回一个 <item-not-found/> 错误.
例子 56. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
坏的订阅ID
如果一个订阅标识符关联于某个订阅项, 取消订阅请求必须包含一个适当的'subid'属性. 如果这个取消订阅请求包含一个 SubID 但是该节点不支持 SubIDs (或该订阅者第一次并没有使用 SubID 来订阅), 服务应该忽略这个 SubID 并简单地取消订阅这个实体. 如果订阅者以前使用一个 SubID 来订阅但是取消订阅申请包含一个不合法或非当前订阅者的 SubID , 服务必须返回一个<not-acceptable/>错误, 它应该也包含一个<invalid-subid/>的pubsub特有的错误条件.
例子 57. 非法的订阅标识符
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <invalid-subid xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
配置订阅选项
实现可以允许订阅者们配置订阅选项. 实现应该使用 数据表单 (Data Forms)协议来实现这个配置(无论如何, 一个带外机制如web界面也可能被提供).
声明支持
如果一个服务支持订阅选项, 它必须在它对"disco#info"查询的应答中声明(在应答中包含一个feature,其'var'属性为"pubsub#subscription-options").
例子 58. Pubsub服务指示支持订阅选项
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='feature1'> <query xmlns='http://jabber.org/protocol/disco#info'> ... <feature var='http://jabber.org/protocol/pubsub#subscription-options'/> ... </query> </iq>
请求
一个订阅者通过在IQ-get节中包含一个<options/>元素来请求订阅选项.
例子 59. 订阅者请求订阅选项表单
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='options1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='francisco@denmark.lit'/> </pubsub> </iq>
成功场景
如果请求被成功的处理, 服务必须应答选项.
例子 60. 服务应答选项表单
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='francisco@denmark.lit'> <x xmlns='jabber:x:data' type='form'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> <field var='pubsub#deliver' type='boolean' label='Enable delivery?'> <value>1</value> </field> <field var='pubsub#digest' type='boolean' label='Receive digest notifications (approx. one per day)?'> <value>0</value> </field> <field var='pubsub#include_body' type='boolean' label='Receive message body in addition to payload?'> <value>false</value> </field> <field var='pubsub#show-values' type='list-multi' label='Select the presence types which are allowed to receive event notifications'> <option label='Want to Chat'><value>chat</value></option> <option label='Available'><value>online</value></option> <option label='Away'><value>away</value></option> <option label='Extended Away'><value>xa</value></option> <option label='Do Not Disturb'><value>dnd</value></option> <value>chat</value> <value>online</value> </field> </x> </options> </pubsub> </iq>
注意: 前述的例子展示了一些(但不是所有)的可以(MAY)被提供的可能的配置选项. 如果一个实现使用 数据表单 (Data Forms)协议提供了这些选项, 它必须使用那些在和 'http://jabber.org/protocol/pubsub' 命名空间相关的XMPP注册处中注册了的字段(以上初步展示了那些字段, 并且在本文的 pubsub#subscribe_options FORM_TYPE 描述了它们, 但是不能(MUST NOT)被当作规范, 因为 XMPP注册处以后还可以在不改变本文的情况下标准化更多的字段).
注意: 很多相关的数据表单字段有一个 "boolean" 类型并且必须被有效处理. 15
失败场景
很多原因可以导致选项请求失败:
- 请求实体没有足够的权限来修改指定的JID的订阅选项.
- 请求实体(或指定的订阅者)未曾订阅.
- 请求没有同时指定 NodeID 和订阅者的 JID.
- 请求没有指定一个订阅ID但是它是必需的.
- 请求指定了一个订阅项ID但不是合法的或当前的.
- 订阅选项不支持.
- 节点不存在.
更完整的错误场景描述如下.
权限不足
请求订阅选项的时候, 订阅者必须指定向节点订阅的 JID 并且应该指定一个节点(如果没有指定节点, 即 通过 node="", 服务必须认为请求实体希望为它的订阅项向根集合节点请求订阅选项; 详见 XEP-0248 ).
服务必须验证提出请求的实体已经被授权为订阅的实体设置订阅选项. 如果订阅者的JID的格式是(<localpart@domain.tld/resource> 或 <domain.tld/resource>), 服务必须比较两个JID的(<localpart@domain.tld> 或 <domain.tld>)部分以确保他们是匹配的. 如果两个JID的纯JID部分不匹配并且请求实体没有被授权修改这个JID的订阅选项(例如, 因为它不是一个服务范围内的管理员或授权代理), 服务必须返回一个<forbidden/>错误.
例子 61. 请求实体没有足够的权限修改订阅选项
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
无该订阅者
如果请求实体(或指定的订阅者, 如果不同的话) 未曾订阅, 服务必须返回一个 <unexpected-request/> 错误, 它也应该包括一个 <not-subscribed/> 的 pubsub特有的错误条件.
例子 62. 没有这个订阅者
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options1'> <error type='modify'> <unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
要求订阅者JID
如果订阅者没有指定一个订阅者JID, 服务必须返回一个<bad-request/>错误, 它也应该包含一个<jid-required/>的pubsub特有的错误条件.
例子 63. 订阅者JID未指定
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <jid-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
要求订阅ID
如果一个订阅标识符关联于某个订阅, 为了服务能够区分来自同一实体的订阅,在订阅请求中必须带上'subid'属性. 如果'subid'是必需的但未被提供, 服务必须返回一个<bad-request/>错误, 它也应该包含一个<subid-required/>的pubsub特有的错误条件.
例子 64. 要求SubID
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <subid-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
错误的订阅ID
如果一个订阅标识符关联于某订阅, 但请求包含的 SubID 不合法或不是当前订阅者的, 服务必须返回一个<not-acceptable/>错误, 它也应该包含一个<invalid-subid/>的pubsub特有的错误条件.
例子 65. 非法的订阅标识符
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='unsub1'> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <invalid-subid xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
不支持订阅选项
如果节点或服务不支持订阅选项, 服务必须应答一个<feature-not-implemented/>错误, 指定一个<unsupported/>的pubsub特有的错误条件和一个"subscription-options"特性.
例子 66. 不支持订阅选项
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='subscription-options'/> </error> </iq>
节点不存在
如果节点不存在, pubsub服务必须返回一个<item-not-found/>错误.
例子 67. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
表单提交
接收了配置表单之后, 请求实体应该提交这个表单来更新这个实体对于那个节点的订阅选项.
例子 68. 订阅者提交完成的选项表单
<iq type='set' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='options2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <options node='princely_musings' jid='francisco@denmark.lit'> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> <field var='pubsub#deliver'><value>1</value></field> <field var='pubsub#digest'><value>0</value></field> <field var='pubsub#include_body'><value>false</value></field> <field var='pubsub#show-values'> <value>chat</value> <value>online</value> <value>away</value> </field> </x> </options> </pubsub> </iq>
表单处理
成功
如果服务能成功处理提交的表单, 它必须应答成功.
例子 69. 服务应答成功
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options2'/>
失败
如果订阅者尝试设置一组非法的选项, 服务必须应答一个<bad-request/>错误.
例子 70. 服务对于请求非法选项应答错误
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='options2'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <invalid-options xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
其他适用于获取订阅选项时发生的错误也同样适用于设置订阅选项的情形.
订阅并配置
大家直到, 如果一个服务支持订阅选项, 一个实体可以在同一个节中订阅和提供订阅选项.
注意: <options/>元素必须跟随在<subscribe/>元素之后并且不能(MUST NOT)拥有一个'node'属性或'jid'属性, 因为<subscribe/>元素的'node'属性值指明了期望的NodeID而<subscribe/>元素的'jid'属性值指明了订阅者的JID; 如果这些值中的任何一个违规了, 服务必须(MUST)返回一个<bad-request/>错误.
例子 71. 实体订阅节点并设置选项
<iq type='set' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscribe node='princely_musings' jid='francisco@denmark.lit'/> <options> <x xmlns='jabber:x:data' type='submit'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> <field var='pubsub#deliver'><value>1</value></field> <field var='pubsub#digest'><value>0</value></field> <field var='pubsub#include_body'><value>false</value></field> <field var='pubsub#show-values'> <value>chat</value> <value>online</value> <value>away</value> </field> </x> </options> </pubsub> </iq>
当服务通知客户端成功时, 它应该包含一个类型为"result"的数据表单来通知客户端配置选项的结果.
例子 72. 服务应答成功(包含配置选项)
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <subscription node='princely_musings' jid='francisco@denmark.lit' subid='ba49252aaa4f5d320c24d3766f0bdcade78c78d3' subscription='subscribed'/> <options> <x xmlns='jabber:x:data' type='result'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> <field var='pubsub#deliver'><value>1</value></field> <field var='pubsub#digest'><value>0</value></field> <field var='pubsub#include_body'><value>false</value></field> <field var='pubsub#show-values'> <value>chat</value> <value>online</value> <value>away</value> </field> </x> </options> </pubsub> </iq>
请求缺省订阅配置选项
一个实体可能想请求关于缺省订阅配置的信息. 对此特性的支持是可选的.
请求
要获得一个节点的缺省订阅选项, 实体必须发送一个空的<default/>元素到该节点; 在应答里面, 该节点应该返回缺省的订阅选项.
例子 73. 实体请求缺省订阅配置选项
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='def1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <default node='princely_musings'/> </pubsub> </iq>
注意: 这里的命名空间是 'http://jabber.org/protocol/pubsub' (不是用于获取缺省节点配置选项的 'http://jabber.org/protocol/pubsub#owner' ).
服务本身也可以有缺省订阅配置选项. 要获得一个服务的缺省订阅配置选项的所有(叶子)节点, 实体必须发送一个空的<default/>元素但是不指定节点; 在应答中, 该服务应该返回缺省订阅选项.
例子 74. 实体请求缺省订阅配置选项
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='def2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <default/> </pubsub> </iq>
获取集合节点的缺省订阅配置选项的过程参见 XEP-0248 .
成功场景
如果没有发生错误, 该节点必须返回缺省的订阅配置选项.
例子 75. 服务应答缺省订阅配置选项
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='def1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <default node='princely_musings'> <x xmlns='jabber:x:data' type='result'> <field var='FORM_TYPE' type='hidden'> <value>http://jabber.org/protocol/pubsub#subscribe_options</value> </field> <field var='pubsub#deliver'><value>1</value></field> <field var='pubsub#digest'><value>0</value></field> <field var='pubsub#include_body'><value>false</value></field> <field var='pubsub#show-values'> <value>chat</value> <value>online</value> <value>away</value> </field> </x> </default> </pubsub> </iq>
错误场景
有很多原因使得请求缺省订阅配置选项失败:
- 服务不支持订阅配置.
- 服务不支持获取缺省订阅选项.
对这些错误的完整描述见接下来的章节.
不支持节点配置
如果节点不支持订阅配置, 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个 "subscription-options" 特性.
例子 76. 服务不支持订阅配置
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='def1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='subscription-options'/> </error> </iq>
不支持获取缺省订阅配置
如果节点不支持或缺缺省明月配置选项, 它必须返回一个<feature-not-implemented/>错误, 指定一个pubsub特有的错误条件<unsupported/>和一个特性 "retrieve-default-sub" .
例子 77. 服务不支持获取缺省订阅配置选项
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='def1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='retrieve-default-sub'/> </error> </iq>
从节点获取条目
选择持久化条目的pubsub实现可以允许实体从一个节点请求已有的条目(例如, 一个实体可能希望在成功订阅之后这样做以接收这个节点历史上发布的所有条目).
许可
服务必须遵循节点访问模式来决定是否向请求它们的实体返回这些条目. 具体来说:
- 如果访问模式是"open", 服务应该允许任何实体(无论是否订阅)获取条目.
- 如果访问模式是"presence", 服务应该允许任何已订阅这个所有者的出席信息的实体获取条目.
- 如果访问模式是"roster", 服务应该允许任何已订阅这个所有者的出席信息并处于适当的名册组中的实体获取条目.
- 如果访问模式是"authorize"或"whitelist", 服务必须只允许已订阅的实体来获取条目.
(In addition, a service MUST always allow the node owner to retrieve items from a node and SHOULD always allow a publisher to do so.)
对于将来的访问模式的需求, 唯一的例外应该是强制本地隐私和安全策略, 更全面的描述见本文的安全事项. (另外, 一个服务必须总是允许节点所有者从一个节点获取条目并且应该总是允许一个发布者这样做.)
请求所有条目
订阅者可以通过仅仅不加限制地指明节点ID来请求所有的条目.
例子 78. 订阅者请求所有条目
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='items1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'/> </pubsub> </iq>
返回所有条目
服务然后应该返回所有发布到这个节点的条目, 尽管它可以截取结果(如果已发行的条目数量太多的话,参见下一节),很自然的它不能返回那些已删除的,过期的,等等条目.
例子 79. 服务返回所有条目
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='368866411b877c30064a5f62b917cffe'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>The Uses of This World</title> <summary> O, that this too too solid flesh would melt Thaw and resolve itself into a dew! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-12T17:47:23Z</published> <updated>2003-12-12T17:47:23Z</updated> </entry> </item> <item id='3300659945416e274474e469a1f0154c'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Ghostly Encounters</title> <summary> O all you host of heaven! O earth! what else? And shall I couple hell? O, fie! Hold, hold, my heart; And you, my sinews, grow not instant old, But bear me stiffly up. Remember thee! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-12T23:21:34Z</published> <updated>2003-12-12T23:21:34Z</updated> </entry> </item> <item id='4e30f35051b7b8b42abe083742187228'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Alone</title> <summary> Now I am alone. O, what a rogue and peasant slave am I! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-13T11:09:53Z</published> <updated>2003-12-13T11:09:53Z</updated> </entry> </item> <item id='ae890ac52d0df67ed7cfdf51b644e901'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </items> </pubsub> </iq>
返回一些条目
一个节点可能有非常多的条目,在这种情况下在应答一个条目请求时返回所有条目是一个问题. 这种情况下,服务应该返回一些条目并提示条目列表已经被截断了,包含一个结果集管理 16 记号.
例子 80. 服务通过结果集管理返回一些条目
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='368866411b877c30064a5f62b917cffe'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>The Uses of This World</title> <summary> O, that this too too solid flesh would melt Thaw and resolve itself into a dew! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-12T17:47:23Z</published> <updated>2003-12-12T17:47:23Z</updated> </entry> </item> <item id='3300659945416e274474e469a1f0154c'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Ghostly Encounters</title> <summary> O all you host of heaven! O earth! what else? And shall I couple hell? O, fie! Hold, hold, my heart; And you, my sinews, grow not instant old, But bear me stiffly up. Remember thee! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-12T23:21:34Z</published> <updated>2003-12-12T23:21:34Z</updated> </entry> </item> <item id='4e30f35051b7b8b42abe083742187228'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Alone</title> <summary> Now I am alone. O, what a rogue and peasant slave am I! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-13T11:09:53Z</published> <updated>2003-12-13T11:09:53Z</updated> </entry> </item> </items> <set xmlns='http://jabber.org/protocol/rsm'> <first index='0'>368866411b877c30064a5f62b917cffe</first> <last>4e30f35051b7b8b42abe083742187228</last> <count>19</count> </set> </pubsub> </iq>
返回最近发布的条目
即使服务或节点不支持持久化条目, 它可以返回最近发布的条目.
例子 81. 服务返回最近发布的条目
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </items> </pubsub> </iq>
只返回通知
一个服务可以返回事件通知而不包含载荷(例如, 为了节省带宽). 如果这样, 该客户端可以请求一个特定的条目(使用 ItemID)以获取载荷. 当一个实体通过ItemID请求条目, 实现必须允许在该请求中指定多个条目.
例子 82. 订阅者通过ItemID请求特定条目
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='items3'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='368866411b877c30064a5f62b917cffe'/> <item id='4e30f35051b7b8b42abe083742187228'/> </items> </pubsub> </iq>
例子 83. 服务发送被请求的条目(们)
<iq type='result' from='pubsub.shakespeare.lit' id='items3'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='368866411b877c30064a5f62b917cffe'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>The Uses of This World</title> <summary> O, that this too too solid flesh would melt Thaw and resolve itself into a dew! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-12T17:47:23Z</published> <updated>2003-12-12T17:47:23Z</updated> </entry> </item> <item id='4e30f35051b7b8b42abe083742187228'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Alone</title> <summary> Now I am alone. O, what a rogue and peasant slave am I! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-13T11:09:53Z</published> <updated>2003-12-13T11:09:53Z</updated> </entry> </item> </items> </pubsub> </iq>
请求最近的条目
一个服务可以允许实体使用 'max_items' 属性请求最近的N个条目 . 当使用了 max_items , 实现应该返回N个最近的(和N个最旧的相反)条目. (注意: 本协议的未来版本可能推荐使用 XEP-0059 取代 'max_items' 属性.)
例子 84. 订阅者请求两个最近的条目
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='items2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings' max_items='2'/> </pubsub> </iq>
例子 85. 服务返回两个最近的条目
<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items2'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='4e30f35051b7b8b42abe083742187228'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Alone</title> <summary> Now I am alone. O, what a rogue and peasant slave am I! </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32396</id> <published>2003-12-13T11:09:53Z</published> <updated>2003-12-13T11:09:53Z</updated> </entry> </item> <item id='ae890ac52d0df67ed7cfdf51b644e901'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </items> </pubsub> </iq>
请求一个特定条目
订阅者可以通过指定节点ID和适当的ItemID请求一个特定条目.
例子 86. 订阅者请求一个特定条目
<iq type='get' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit' id='items3'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'/> </items> </pubsub> </iq>
该服务接着将返回那个特定的条目,如果可用的话.
错误场景
有很多种原因可能导致条目接收请求失败:
- 请求实体对同一个节点有多个订阅但是没有指定一个订阅ID.
- 请求实体订阅了但是指定了一个非法的订阅项ID.
- 节点不返回条目给未订阅的实体而请求实体确实未曾订阅.
- 服务或节点不支持持久化条目并且不返回最后发布的条目.
- 服务或节点不支持条目获取.
- 节点有一个"presence"访问模式而请求实体没有订阅所有者的出席信息.
- 节点有一个"roster"访问模式而请求实体不在授权的名册组之一.
- 节点有一个"whitelist"访问模式而请求实体不在白名单中.
- 服务或节点需要付费才允许获取条目.
- 请求实体被禁止从节点获取条目(例如, 因为有一个排斥者的从属关系).
- 节点不存在.
这些错误的完整描述如下.
要求订阅ID
如果请求实体对同一个节点有多个订阅但是没有指定一个订阅ID, 服务必须返回一个<bad-request/>错误给订阅者, 它也应该包含一个<subid-required/>的pubsub特有的错误条件.
例子 87. 实体未指定SubID
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='modify'> <bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <subid-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
非法的订阅ID
如果请求实体订阅了但是指定了一个非法的订阅ID, 服务必须返回一个<not-acceptable/>错误给订阅者, 它也应该包含一个<invalid-subid/>的pubsub特有的错误条件.
例子 88. 实体指定了非法的SubID
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='modify'> <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <invalid-subid xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
实体未订阅
如果节点未返回条目给未订阅的实体并且请求实体未曾订阅(包括已有一个未决的订阅的情形), 服务必须返回一个<not-authorized/>错误给订阅者, 它也应该包含一个<not-subscribed/>的pubsub特有的错误条件.
例子 89. 实体未订阅
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='auth'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
不支持持久化条目
如果服务或节点不支持持久化条目且没有返回最后发行的条目, 服务必须返回一个<feature-not-implemented/>错误给订阅者, 指定一个pubsub特有的错误条件<unsupported/>以及一个特性"persistent-items".
例子 90. 不支持持久化条目
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='persistent-items'/> </error> </iq>
不支持获取条目
如果服务或节点不支持获取条目(例如, 因为节点是一个如 XEP-0248 所述的集合节点), 服务必须返回一个<feature-not-implemented/>错误给订阅者, 指明一个pubsub特有的错误条件<unsupported/>以及一个特性"retrieve-items".
例子 91. 不支持获取条目
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='cancel'> <feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='retrieve-items'/> </error> </iq>
要求订阅出席信息
对于访问模式为"presence"的节点, 如果请求实体没有订阅所有者的出席信息那么pubsub服务必须应答一个<not-authorized/>错误, 它还应该包含一个pubsub特有的错误条件<presence-subscription-required/>.
例子 92. 实体没有被授权获取条目(要求订阅出席信息)
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='auth'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <presence-subscription-required xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
不在名册组
对于访问模式为"roster"的节点, 如果请求实体不在授权名册组中那么pubsub服务必须应答一个<not-authorized/>错误, 它也应该包含一个pubsub特有的错误条件<not-in-roster-group/>.
例子 93. 实体没有被授权获取条目(不在名册组中)
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='auth'> <not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <not-in-roster-group xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
不在白名单
对于访问模式为"whitelist"的节点, 如果请求实体不在白名单中那么服务必须返回一个<not-allowed/>错误, 指明一个pubsub特有的错误条件<closed-node/>.
例子 94. 节点为白名单模式
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='cancel'> <not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> <closed-node xmlns='http://jabber.org/protocol/pubsub#errors'/> </error> </iq>
要求付费
商业部署可能希望把订阅者链接到一个付费客户数据库. 如果订阅者需要付费才能从那个节点获取条目(例如, 如果订阅者不在客户数据库或客户的帐目没有付清), 服务应该返回一个<payment-required/>错误给订阅者.
例子 95. 需要付费才能获取条目
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='auth'> <payment-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
被禁止
如果请求实体被禁止获取条目(例如, 因为有一个被排斥者的从属关系), 服务必须返回一个<forbidden/>错误给订阅者.
例子 96. 请求实体被禁止
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='auth'> <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
节点不存在
如果节点不存在, 服务应该返回一个<item-not-found/>错误给订阅者.
例子 97. 节点不存在
<iq type='error' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='items1'> <error type='cancel'> <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
无此条目
如果该节点没有条目或请求的那个条目不存在, 服务应该返回一个类型为"result"并带有一个空的<items/>元素的IQ节.
例子 98. 无此条目
<iq from='pubsub.shakespeare.lit' id='items1' to='francisco@denmark.lit/barracks' type='result'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <items node='princely_musings'/> </pubsub> </iq>
发布者用例
发布条目到节点
请求
一个被允许向一个节点发布条目的实体 (即.,一个发布者或一个所有者) 可以在任何时候通过发送一个包含一个pubsub子元素<publish/>的 IQ-set 节给服务来做到这一点.
语法如下:
例子如下.
例子 99. 发布者带ItemID发布一个条目
<iq type='set' from='hamlet@denmark.lit/blogbot' to='pubsub.shakespeare.lit' id='publish1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <publish node='princely_musings'> <item id='bnd81g37d61f49fgn581'> <entry xmlns='http://www.w3.org/2005/Atom'> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel='alternate' type='text/html' href='http://denmark.lit/2003/12/13/atom03'/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </publish> </pubsub> </iq>
成功场景
如果pubsub服务能成功处理请求, 它必须通知发布者成功了. 如果发布请求不包含一个ItemID, IQ-result 应该包含一个空的<item/>元素来指定已发布的条目的ItemID.
例子 100. 服务应答成功
<iq type='result' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/blogbot' id='publish1'> <pubsub xmlns='http://jabber.org/protocol/pubsub'> <publish node='princely_musings'> <item id='ae890ac52d0df67ed7cfdf51b644e901'/> </publish> </pubsub> </iq>
注意: 如果发布者之前用同一个ItemID发布了一个条目, 该请求的成功处理意味着服务必须以新条目覆盖那个旧条目并做如下处理.
pubsub服务接着必须发送一个事件通知给每个按标准可以接收事件通知的实体(典型的是对于每个已批准的订阅者, 尽管在其他上下文中实体也可以接收事件通知,如通知触发器总结的那样). 每个由pubsub服务生成的<message/>节应该拥有一个值为唯一的'id'属性这样该服务能正确地追踪任何和通知相关的可能发生的错误(见本文的处理通知相关的错误). 根据节点配置, 事件通知要么包含要么不包含在和, 如下所示.
注意: 为了做到如本文从节点删除条目所述的条目删除的授权, 支持持久化条目的实现应该存储该条目(如果该节点是如此配置的)并维护一个发布者的记录.
注意: 如果该服务或节点已被配置,那么该节点上缓存的条目数有一个最大值,如果当一个条目被发布时缓存数量已经达到了最大值,该服务必须删除现有的条目之一. 推荐服务遵循 "先进先出" 规则并删除最旧的条目. 根据节点配置, 删除一个现有的条目可能(MAY)导致发送一个删除通知给订阅者.