XEP-0030

来自Jabber/XMPP中文翻译计划
2010年5月20日 (四) 17:15Admin (讨论 | 贡献)的版本
跳转到: 导航, 搜索


本文的英文原文来自XEP-0030

XEP-0030:服务发现

摘要: 本文定义了一个XMPP协议扩展用于发现其他XMPP实体的信息.有两种信息可以被发现:(1) 一个实体的身份和能力, 包括它支持的协议和特性; (2) 和一个实体相关的条目, 例如一个多用户聊天服务的房间列表.

作者: Joe Hildebrand, Peter Millard, Ryan Eatmon, Peter Saint-Andre

XMPP扩展协议的版权(1999-2008)归XMPP标准化基金会(XSF)所有

版权: © 1999 - 2010 XMPP标准化基金会(XSF). 参见法律通告.

状态: 最终

类型: 标准跟踪

版本: 2.4

最后更新日期: 2008-06-06

注意: 这里定义的协议是XMPP标准化基金会的一个最终标准,对于实现和布署来说可以被认为是一个稳定技术.

目录

绪论

在Jabber网络中发现实体相关信息的能力是极有价值的。这些信息包括实体支持的特性或支持的协议、实体的类型或身份、同原实体以某种方式相联系的另外一些实体等(这些实体通常当作“父”实体的“子”实体)。虽然XMPP Core 1并没有定义怎样做的机制,但在Jabber社区内,过去已经有几个协议用在了服务发现上,特别是 Jabber Browsing 2Agent Information 3上。然而,这些协议总使人感觉不那么合适,其原因如下:

  1. Jabber Browsing 和 Agent Information 都不容易扩展。例如,在XEP-0011中列出JID类型的种类及子类都明确地定义为唯一的正式种类,向列表加入新的JID类型都要修改XEP-0011。而Jabber Browsing规范不允许用非正式种类和以‘x-’打头的类型,这会产生移植问题。这种适应性的缺乏违反了Jabber社区的核心XMPP协议设计原则 4之一 。
  2. 在 Agent Information 协议中,没有声明所支持的特性的方法。虽然Jabber Browsing包含了这样的机制,但表达一个特性的有效性的唯一方法是声明一个所支持的名字空间。然而,有些特性也许并非唯一地对应一个协议名字空间,它只是特性的一个实现,但并是唯一的一个。
  3. 一个Jabber Browsing 结果返回一个组合,由(1)Jabber实体支持的名字空间,(2)与Jabber实体相关联的条目,(3)相关条目支持的名字空间组成。这种方法混淆了信息级别,要求父节点知晓子节点的一切,也就引入了极大的混乱。
  4. 在Jabber Browsing 和 Agent Information中,要求条目们(items)必须可寻址为JIDs;然而,这在有些应用中是做不到的。

本文档旨在克服Jabber Browsing和Agent Information这两个协议的弱点。其结果就是一个用于服务发现的标准追踪协议(通常缩写为“disco”,就和一些熟悉的协议,如 SOAP 5一样)。

需求

在作者的头脑中构思的服务发现协议要满足下面的需求:

  • 协议 必须 支持它所取代的协议(Jabber Browsing 和 Agent Information)之全部功能
  • 有三种关于一个实体的信息需要发现:
  1. 它的基本身份(类型和/或目录)
  2. 它提供的特性和支持的协议
  3. 与实体相关联的任何附加条目,无论它们是否能定址为JID

三种信息都 必须 支持,但前两种信息都与实体本身有关,而第三种信息与实体关联的条目有关;因此只需要两种不同的查询类型。

  • 子条目信息的发现 必须 通过向它自己,而不是向父实体,发送单独的发现请求实现。(其后果之一是,要发现整个三种信息,需要多个请求/响应对,以“遍历整个信息树”)。
  • 身份和特性列表 必须 是柔性的。
  • 协议本身 必须 是可扩展的。

发现Jabber实体的信息

基本协议

一个发出请求的实体会想发现网络中另一个实体的信息。想得到的信息一般分为两种:

  1. 对象实体的身份(identity)。在disco中,一个实体的身份被分成种类(category)(服务器、客户端、网关、目录等等)及其种类中的特殊类型(IM服务器、电话对处理的客户端、MSN网关对AIM网关、用户目录对聊天室目录等等)。这个信息帮助请求实体测定服务组或服务“桶”,以使实体最恰当地放置其中(例如,或许用个合适的图标把实体显示成GUI)。一个实体 可以 有多个身份。当提供多个identity元素的时候,每个identity元素的 name 属性 应该 有相同的值。
  2. 目标实体提供的特性和支持的协议。这个信息帮助请求实体测定对目标实体可以做什么样的动作(注册、搜索、联合等等),实体支持什么样的协议,以及是否有感兴趣的特性类型(例如,为了特性协商的目的)。

为了发现这样的信息,请求实体 必须 向目标实体的JID发送类型为“get”的IQ节,包含一个用命名空间'http://jabber.org/protocol/disco#info'标识的空的<query/>元素('to'的地址是 必需的必须 包含有效的JID;<query>元素的'node'属性是 可选的 ,本文中~~信息节点及细节节点~~一节对此有说明)。

例1. 查询信息

<iq type='get'
    from='romeo@montague.net/orchard'
    to='plays.shakespeare.lit'
    id='info1'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>

然后目标实体 必须 返回一个IQ结果,或者返回一个错误(见本文错误条件)。结果 必须 包含用命名空间'http://jabber.org/propocol/disco#info'标识的<query/>元素,元素中依次包含一个或多个<identity/>元素及一个或多个<feature/>元素。(注意:每个实体 必须 至少有一个identity,每个实体 必须 至少支持‘http://jabber.org/protocol/disco#info’特性;然而,并不要求一个实体一定返回一个结果,它也 可以 返回一个错误,最可能的是<feature-not-implemented/>或<service-unavilable/>,虽然其他出错条件也是可以的)。

每个<identity/>元素 必须 拥有'category'和'type'属性,用来说明实体的种类, 而且可以 MAY 拥有一个 'name' 属性为这个实体指定一个自然语言的名字; 这个 <identity/> 元素也可以 MAY 拥有一个标准的 'xml:lang' 属性, 它使实体能返回本地化的结果,如果必要的话(即, <query/> 元素可以 MAY 包含多个 <identity/> 元素,使用相同的 category+type 但是不同的 'xml:lang' 值;无论如何 <query/> 元素不能 MUST NOT 以相同的 category+type+xml:lang 和不同的 'name' 值包含多个 <identity/> 元素,).

每个<feature/>元素 必须 拥有一个'var'属性,它的值是协议的命名空间或是实体提供的其他特性。

如果象本文中注册事项一节说明的那样,种类/类型的值和特性值都在公共登记处注册了的话就更好了。

例2. 信息请求的结果集

<iq type='result'
    from='plays.shakespeare.lit'
    to='romeo@montague.net/orchard'
    id='info1'>
  <query xmlns='http://jabber.org/protocol/disco#info'>
    <identity
        category='conference'
        type='text'
        name='Play-Specific Chatrooms'/>
    <identity
        category='directory'
        type='chatroom'
        name='Play-Specific Chatrooms'/>
    <feature var='http://jabber.org/protocol/disco#info'/>
    <feature var='http://jabber.org/protocol/disco#items'/>
    <feature var='http://jabber.org/protocol/muc'/>
    <feature var='jabber:iq:register'/>
    <feature var='jabber:iq:search'/>
    <feature var='jabber:iq:time'/>
    <feature var='jabber:iq:version'/>
  </query>
</iq>

如果指定目标实体的JID不存在,服务器或其他认证实体 应该 返回一个<item-not-found/>错误,除非如果这么做会破坏在XMPP CoreXMPP IM 6中说明的隐私和安全事项,或是破坏本地隐私和安全策略(见本文[XEP-0030#安全事项|安全事项]]):

例3. 目标实体不存在

<iq type='error'
    from='plays.shakespeare.lit'
    to='romeo@montague.net/orchard'
    id='info1'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
  <error code='404' type='cancel'>
    <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
  </error>
</iq>

如果隐私和安全事项或策略阻止服务器或其他授权实体返回<item-not-found/>错误,取而代之它 应该 返回一个<service-unavailable/>错误:

例4. 服务不可用

<iq type='error'
    from='plays.shakespeare.lit'
    to='romeo@montague.net/orchard'
    id='info1'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
  <error code='503' type='cancel'>
    <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
  </error>
</iq>

当一个实体向一个由服务器做宿主的纯JID(<account@domain.tld>)发送disco#info请求的时候,这个服务器本身 必须 代表寄宿的帐号回应一个IQ错误或IQ结果。有关访问这些功能的重要规则,见本文的安全事项一节。特别地,为响应向一个没有节点的纯JID发出的disco#info请求,如果访问没被拒绝,服务器 应该' 为这个纯JID返回一个IQ结果,里面的主 identity 应该 有具有合适类型的“account”种类,类型在 Service Discovery Identities 注册中说明(最可能的是类型"registered")。注意:这使得那些已授权或被信任的实体能发现帐号帐号是否存在及帐号的类型(例如,在IM系统中,在把帐号加入联系人列表之前检测它是否存在)。

例5. 从纯JID那里请求信息

<iq type='get'
    from='shakespeare.lit'
    to='juliet@capulet.com'
    id='info2'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>

这里我们假定shakespeare.lit被capulet.com信任,而帐号<juliet@capulet.com>是个注册帐号:

例 6. 服务器替纯JID应答

<iq type='result'
    from='juliet@capulet.com'
    to='shakespeare.lit'
    id='info2'>
  <query xmlns='http://jabber.org/protocol/disco#info'>
    <identity category='account' type='registered'/>
  </query>
</iq>

向关联实体发出的查询结果会不同,或能得到更详细的信息。一个例子是向一个特别的会议室而不是父实体的会议服务发出查询:

例7. 查询指定的会议室

<iq type='get'
    from='juliet@capulet.com/balcony'
    to='balconyscene@plays.shakespeare.lit'
    id='info3'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>
 
<iq type='result'
    from='balconyscene@plays.shakespeare.lit'
    to='juliet@capulet.com/balcony'
    id='info3'>
  <query xmlns='http://jabber.org/protocol/disco#info'>
    <identity
        category='conference'
        type='text'
        name='Romeo and Juliet, Act II, Scene II'/>
    <feature var='http://jabber.org/protocol/disco#info'/>
    <feature var='http://jabber.org/protocol/muc'/>
    <feature var='http://jabber.org/protocol/feature-neg'/>
    <feature var='muc-password'/>
    <feature var='muc-hidden'/>
    <feature var='muc-temporary'/>
    <feature var='muc-open'/>
    <feature var='muc-unmoderated'/>
    <feature var='muc-nonanonymous'/>
  </query>
</iq>

另一个例子是向一个带明确资源连接的IM用户发出查询:

例 8. 查询连接资源的更详细的信息

<iq type='get'
    from='juliet@capulet.com/balcony'
    to='romeo@montague.net/orchard'
    id='info4'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>
 
<iq type='result'
    from='romeo@montague.net/orchard'
    to='juliet@capulet.com/balcony'
    id='info4'>
  <query xmlns='http://jabber.org/protocol/disco#info'>
    <identity
        category='client'
        type='pc'
        name='Gabber'/>
    <feature var='jabber:iq:time'/>
    <feature var='jabber:iq:version'/>
  </query>
</iq>

信息节点

一个 disco#info 也查询可以 MAY 被定向到特定的关联到一个JID的节点标识符, 尽管节点的主要用途是成为条目节点 Items Nodes 而不是信息节点 info nodes:

例子 9. 查询一个特定的JID和node的组合

<iq type='get'
    from='romeo@montague.net/orchard'
    to='mim.shakespeare.lit'
    id='info3'>
  <query xmlns='http://jabber.org/protocol/disco#info' 
         node='http://jabber.org/protocol/commands'/>
</iq>

如果请求包含 'node' 属性, 应答必须 MUST 镜像这个指定的 'node' 属性以确保请求和应答之间的相关性.

例子 10. JID+node结果

<iq type='result'
    from='mim.shakespeare.lit'
    to='romeo@montague.net/orchard'
    id='info3'>
  <query xmlns='http://jabber.org/protocol/disco#info' 
         node='http://jabber.org/protocol/commands'>
    <identity
        category='automation'
        type='command-list'/>
  </query>
</iq>


发现与Jabber实体相关的条目

基本协议

请求实体为了发现一个Jabber实体相关联的条目,它 必须 向目标实体发送类型为"get"的IQ节,其中包含一个空的<query/>元素,受限命名空间为‘http://jabber.org/protocol/disco#items’:

例11. 请求全部细节

<iq type='get'
    from='romeo@montague.net/orchard'
    to='shakespeare.lit'
    id='items1'>
  <query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>

目标实体 必须 或者返回它的公开可用的条目列表,或者返回一个错误。条目列表 必须 是类型为"result"的IQ段,每个条目用<query/>的子元素<item/>来说明,受限命名空间是'http://jabber.org/protocol/disco#items'(子元素<item/> 必须 用'jid'属性指定条目的JID, 可以 用'name'属性说明条目的自然语言名):

例12. 所有条目的结果集

<iq type='result'
    from='shakespeare.lit'
    to='romeo@montague.net/orchard'
    id='items1'>
  <query xmlns='http://jabber.org/protocol/disco#items'>
    <item jid='people.shakespeare.lit'
          name='Directory of Characters'/>
    <item jid='plays.shakespeare.lit'
          name='Play-Specific Chatrooms'/>
    <item jid='mim.shakespeare.lit'
          name='Gateway to Marlowe IM'/>
    <item jid='words.shakespeare.lit'
          name='Shakespearean Lexicon'/>
    <item jid='globe.shakespeare.lit'
          name='Calendar of Performances'/>
    <item jid='headines.shakespeare.lit'
          name='Latest Shakespearean News'/>
    <item jid='catalog.shakespeare.lit'
          name='Buy Shakespeare Stuff!'/>
    <item jid='en2fr.shakespeare.lit'
          name='French Translation Service'/>
  </query>
</iq>

<item/>元素 必须不可 包含XML字符数据,并 应该 是空元素;当然它 可以 在其他命名空间中包含XML数据,如果一个Jabber实现不理解这些数据,则 必须 忽略它。

如果没有条目与实体相关联(或者这些条目并非公开可用的),目标实体必须向请求实体返回一个空的query元素:

例13. 空的结果集

<iq type='result'
    from='shakespeare.lit'
    to='romeo@montague.net/orchard'
    id='items1'>
  <query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>

就请求disco#info来说,当实体向寄生在一个服务器的纯JID(<account@domain.tld>)发送disco#items请求时,宿主服务器自身 必须 代表寄生帐号回应。有关访问这个功能的重要准则,见本文的安全事项一节。特别地,为响应发到一个不带节点的纯JID的disco#info请求,如果访问没被拒决,服务器 应该 返回相关的条目,包括适当的已连接或可用的资源:

例14. 从一个纯JID请求条目

<iq type='get'
    from='shakespeare.lit'
    to='juliet@capulet.com'
    id='items2'>
  <query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>

在这儿我们假设shakespeare.lit被capulet.com信任,帐号<juliet@capulet.com>有两个可用的资源:

例15. 服务器代表纯JID响应

<iq type='result'
    from='juliet@capulet.com'
    to='shakespeare.lit'
    id='items2'>
  <query xmlns='http://jabber.org/protocol/disco#items'>
    <item jid='juliet@capulet.com/balcony'/>
    <item jid='juliet@capulet.com/chamber'/>
  </query>
</iq>

条目节点

与实体相关联的条目有可能不能写成JID地址的形式;这样的例子包括,储存在收件箱的离线消息(见柔性离线消息获取 7)、有Jabber能力的博客中的条目、与一个客户或组件关联的XML-RPC服务、在一个在线交易系统中的可用条目 (例如, 一个目录或拍卖)、位于一个NNTP网关的新闻贴、及由XEP-0060 发布-订阅 8组件 主办的主题等等。为处理这些条目,<item/>元素 可以 有个 可选的 'node'属性来补充这个 必须的 'jid'属性。

节点的属性值可以有或没有语义上的含义;从服务发现的观点来看,一个节点只不过是个与实体相联系的某种东西。为了发现节点更多的东西,请求实体 必须 在查询实体的JID的时候指定节点。如果'node'属性的值有语义上的含义,意味着这个含义是由"使用的协议"或应用提供的,而不是由服务发现协议决定的。不应该 包含节点属性,除非需要提供,或者要发现信息的实体不能直接写成JID地址的形式(就是说,如果相关的条目能写成一个JID就不要包含一个节点)。'node'属性的值 必须不能 为空。

在下面的例子中,用户从一个在线目录服务中请求所有可用的条目:

例16. 请求节点

<iq type='get'
    from='romeo@montague.net/orchard'
    to='catalog.shakespeare.lit'
    id='items2'>
  <query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>

如果有与目标实体相关的条目但它们不能写成JID地址,那么服务 应该 返回一个节点列表(每个<item/>元素 必须 有一个'jid'属性, 应该 有个'node'属性, 可以 有个'name'属性):

例17. 服务返回节点

<iq type='result'
    from='catalog.shakespeare.lit'
    to='romeo@montague.net/orchard'
    id='items2'>
  <query xmlns='http://jabber.org/protocol/disco#items'>
    <item jid='catalog.shakespeare.lit'
          node='books'
          name='Books by and about Shakespeare'/>
    <item jid='catalog.shakespeare.lit'
          node='clothing'
          name='Wear your literary taste with pride'/>
    <item jid='catalog.shakespeare.lit'
          node='music'
          name='Music from the time of Shakespeare'/>
  </query>
</iq>

在返回上面的查询时,也许有更多的节点与"第一级"节点相关联(比如,节点可能是一个类别,有下级条目)。请求实体可以通过向这个JID发送请求,并在请求中说明感兴趣的节点,来进一步查询节点。

例18. 请求更多的节点

<iq type='get'
    from='romeo@montague.net/orchard'
    to='catalog.shakespeare.lit'
    id='items3'>
  <query xmlns='http://jabber.org/protocol/disco#items' 
         node='music'/>
</iq>

然后服务返回与“父”节点相关的更深层的节点。在下面的例子中,服务本身将返回的节点按字母顺序排序,不过这种结构是实现的问题而不是协议的要求。

例19. 服务返回更深层的节点

<iq type='result'
    from='catalog.shakespeare.lit'
    to='romeo@montague.net/orchard'
    id='items3'>
  <query xmlns='http://jabber.org/protocol/disco#items' 
         node='music'>
    <item jid='catalog.shakespeare.lit'
          node='music/A'/>
    <item jid='catalog.shakespeare.lit'
          node='music/B'/>
    <item jid='catalog.shakespeare.lit'
          node='music/C'/>
    <item jid='catalog.shakespeare.lit'
          node='music/D'/>
    .
    .
    .
  </query>
</iq>

如果想要,请求实体可以继续查询更深层的节点:

例20. 请求更深层节点

<iq type='get'
    from='romeo@montague.net/orchard'
    to='catalog.shakespeare.lit'
    id='items4'>
  <query xmlns='http://jabber.org/protocol/disco#items' 
         node='music/D'/>
</iq>

例21. 服务返回更多的节点

<iq type='result'
    from='catalog.shakespeare.lit'
    to='romeo@montague.net/orchard'
    id='items4'>
  <query xmlns='http://jabber.org/protocol/disco#items' 
         node='music/D'>
    <item jid='catalog.shakespeare.lit'
          node='music/D/dowland-firstbooke'
          name='John Dowland - First Booke of Songes or Ayres'/>
    <item jid='catalog.shakespeare.lit'
          node='music/D/dowland-solace'
          name='John Dowland - A Pilgrimes Solace'/>
  </query>
</iq>

节点层次结构

前面的例子显示了节点的层次结构,里面的有些节点是分支(也即包含更深层的节点),有些节点是叶子(即不包含更深层的节点)。 应该 用"hierarchy"这个category来区分这些节点,“分支”和“叶子”表示这个种类category的具体类型type。

如果使用了分层种类,层次结构中的每个节点 必须 标识为要么是分支要么是叶子;然而,因为一个节点 可以 有多个身份,所以任一节点也 可以 有一个除了“层次/分支”或“层次/叶子”结构外的身份。

因此,上面显示的例子中,一个向"music/D"的disco#info请求会产生<identity category='hierarchy' type='branch'/>,而向"<music/D/dowland-firstbooke>"节点发出的请求会产生<identity category='hierarchy' type='leaf'/>(每个节点会产生恰当的附加身份)。

实体和其条目的关系

本节要说明的是一个实体和它的相关条目之间更详细的关系。

一般情况下,由一个实体在disco#items结果中返回的条目 必须 是与实体有隶属关系的条目 --或者是实体直接控制条目本身(比如,实体拥有的Publish-Subscribe节点),或者实体至少能以Jabber网络中规范的方式提供或确保这些条目(例如,群聊房间直接寄宿于一个多用户聊天服务或一个通过网关提供访问的IRC频道)。

这样的关系并不限制所属实体的地址与相关实体地址间的关系。特别地,下面任何一种情况都是非常合适的:

  1. 查询一个实体(JID1)的条目时,接收到可定址为JID的条目列表;每个相关条目都有自己的JID,但这些JID都与JID1不相同。
  2. 查询一个实体(JID1)的条目时,接收到不可定址为JID的条目列表;每个相关条目有各自的JID+Node,其中JID与JID1相同但每个NodeId都是唯一的。
  3. 查询一个实体(JID1+NodeID1)的条目时,接收到可定址为JID的条目列表;每个相关条目有自己的JID,但没有JID与JID1相同。
  4. 查询一个实体(JID1+NodeId1)的条目时,接收到不能定址为JID的细节列表;每个相关条目有自己的JID+node,但是没有JID+node与JID1+NodeID1相同,并且每个NodeID都在相关JID的上下文中都是唯一的。

另外,返回的结果也 可以 是混合型的,这样查询一个JID或JID+node会产生(1)定址为JID的项和(2)定址为JID+node这两种的组合。

考虑一下实体的这种情况,它拥有多个 发布-订阅 节点 --例如,一个人拥有多个音乐演奏家节点。下面的例子演示了disco#items查询及其结果会是什么样子(使用了在User Tune 9中定义的协议):

例22. 用户请求有关实体的音乐

<iq from='juliet@capulet.com/chamber'
    id='items4'
    to='romeo@montague.net'
    type='get'>
  <query xmlns='http://jabber.org/protocol/disco#items' 
         node='http://jabber.org/protocol/tune'/>
</iq>

被查询的实体现在返回一个它控制的 发布-订阅publish-subscribe 节点的列表,每个节点寄宿于不同的发布订阅 pubsub 服务:

例23. 实体返回多项条目

<iq from='romeo@montague.net'
    id='items4'
    to='juliet@capulet.com/chamber'
    type='get'>
  <query xmlns='http://jabber.org/protocol/disco#items' 
         node='http://jabber.org/protocol/tune'>
    <item jid='pubsub.shakespeare.lit'
          name='Romeo&apos;s CD player'
          node='s623nms9s3bfh8js'/>
    <item jid='pubsub.montague.net'
          node='music/R/Romeo/iPod'/>
    <item jid='tunes.characters.lit'
          node='g8k4kds9sd89djf3'/>
  </query>
</iq>

发布可用的条目

这个特性已经被移到定义 XMPP 发行-订阅 技术的 XEP-0060 中去了.

实现注意事项

信息请求数

当发出请求的应用是一个客户端的时候,在用户收到好友名册并收到名册中所有联系人的出席信息(例如, 展示可用)后,它也许想得到所有联系人的服务发现信息。不幸的是,用户的花名册可能很长,结果登录后会向外发出大量的disco#info查询和接收进来大量的disco#info响应。基于可扩展性和带宽利用的原因,并不希望有这种“发现洪流”,因此,客户端应用 应该实体能力 11来确定它们接收到了出席信息的那些实体的能力,而 不应该 向这些实体发送disco#info请求。

请求的条目数

为了获取实体及相关条目的完整信息,发出请求的应用程序需要“遍历”细节树。很自然,这会产生大量的请求和响应。如果条目列表很长的话(比如,超过20条),发出请求的应用程序接下来 不应该 对所有的条目发送请求。日常需要维护大量条目的那些实体(如IRC网关或NNTP服务) 应该 将节点组织成层次结构,并且/或者通过像 [http://www.xmpp.org/extensions/xep-0055.html Jabber Search} 11之类的协议,提供更健壮的搜索能力; 不应该 通过服务发现返回极其巨大的结果集。

响应一致性

本文档推荐但并不要求响应实体对来自不同的请求实体的相同的请求返回一样的结果(比如,一个实体基于对请求实体的信任度或是已知能力的不同,可能返回不同的细节或特性列表)。然而,响应实体 应该 对发送到相同的 JID+node 组合的所有disco#info请求返回相同的<identity/>元素(category+type) 。

错误条件

如果特定的实体(JID或JID+node)不支持disco命名空间,拒绝对特定的请求实体或任何请求实体返回disco结果,它 应该 分别返回适当的错误信息(象<service-unavailable/>,<forbidden/>,或<not-allowed/>等等)。示例如下:

例24. JID+node错误

<iq type='error'
    from='mim.shakespeare.lit'
    to='romeo@montague.net/orchard'
    id='info3'>
  <query xmlns='http://jabber.org/protocol/disco#info' 
         node='http://jabber.org/protocol/commands'/>
  <error code='405' type='cancel'>
    <not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
  </error>
</iq>

根据应用的不同,也可以有其他的错误条件。

下表总结了常见的错误条件,它们在服务发现Service Discovery的上下文中可能有特殊的含义(关于错误条件的语法和语义的信息,见错误条件映射 12)。

表1: 错误条件

条件 原因
<item-not-found/> 指定目标实体的JID或JID+NodeID并不存在,并且依据隐私及安全事项和策略,这一事实可以暴露。
<service-unavailable/> 目标实体不支持这一协议,或者指定的目标实体并不存在,但这一事实因为隐私及安全事项不能暴露。

其他在XMPP Core中说明的错误条件也 可以 返回(<forbidden/>,<not-allowed/>,<not-authorized/>等等),包括应用专有的错误条件。

正象上面说明的那样,如果一个实体没有相关的子条目,它 必须 返回一个空的<query/>元素(而不是一个错误)来响应disco#items请求。

安全事项

当实体暴露(通过disco#info响应)它支持的特定的协议或特性的时候,某些攻击会更容易发生;然而,一般来说,服务发现不会引入新的漏洞,因为恶意实体会通过发送对这些协议的特定的请求而不是服务发现请求,来发现响应实体支持哪些协议和特性。

当响应实体应答从不同的请求实体接收到的服务发现请求的时候,它没有义务返回相同的服务发现响应。 它 可以 在响应前执行授权检测,以决定怎样(或是否)响应。

服务器 必须 小心地控制对任何可能导致目录获取攻击或泻露已连接或可用资源的功能的访问,这些功能由服务器对发送到寄宿其中的纯JID(地址型如account@domain.tld)的disco#info和disco#items请求的响应组成,因为服务器要代表这个帐户响应这样的请求。处理发送到纯JID的服务发现请求时应用如下规则:

1. 为了响应disco#info请求,如果下列条件之一为真,服务器 必须 返回<service-unavailable/>错误:

  1. 目标实体不存在(无论请求指定的是否是一个节点)
  2. 请求实体未经授权从目标实体接收出席信息(也即,出席信息订阅的类型是"both"或"from"),或者请求实体不被信任(例如,在一个信任网络中的另一个服务器)。

2. 为了响应disco#items请求,服务器 必须 返回一个空结果集,如果:

  1. 目标实体不存在(无论请求指定的是否是一个节点)
  2. 请求没有指定节点,唯一的条目是可用的资源(象在RFC3921中定义的那样),并且请求实体未经授权从目标实体接收出席信息(也即,出席信息订阅的类型是"both"或"from"),或者请求实体不被信任(例如,在一个信任网络中的另一个服务器)13

IANA事项

本文档与互联网编号分配授权机构 14无关。

XMPP登记事项

协议命名空间

XMPP Registrar 16在它的协议命名空间注册项中包含了'http://jabber.org/protocol/disco#info'和'http://jabber.org/protocol/disco#items'名字空间

登记处

身份种类及类型登记

XMPP登记处 XMPP Registrar 维护着命名空间'http://jabber.org/protocol/disco#info'里<identity/>元素的属性的'category'和'type'的注册值;见<[1]>

过程

为了向注册项提交新值,注册人将按如下的形式定义XML段,然后将其包含在相关的XMPP扩展协议中或是发送邮件到<registrar@xmpp.org>:

<category>
  <name>the name of the category (all lower-case)</name>
  <desc>a natural-language description of the category</desc>
  <type>
    <name>the name of the specific type (all lower-case)</name>
    <desc>a natural-language description of the type</desc>
    <doc>the document (e.g., XEP) in which this type is specified</doc>
  </type>
</category>

注册人每次可以注册一个以上的种类,每个种类包含在独立的<category/>元素中。每个注册人也可以每次注册一个以上的类型,每个类型包含在独立的<type/>子元素中。已有种类中的新类型的注册必须包含完整的XML片段,但不应包含种类的描述(仅有种类的名称)。

初始提交

本文档定义了一个种类的“层次结构”,它仅包含两个类型:“branch”和“leaf”;相关的注册提交项如下:

<category>
  <name>hierarchy</name>
  <desc>
    An entity that exists in the context of a 
    service discovery node hierarchy.
  </desc>
  <type>
    <name>branch</name>
    <desc>
      A "container node" for other entities in a 
      service discovery node hierarchy.
    </desc>
    <doc>XEP-0030</doc>
  </type>
  <type>
    <name>leaf</name>
    <desc>
      A "terminal node" in a service discovery 
      node hierarchy.
    </desc>
    <doc>XEP-0030</doc>
  </type>
</category>

特性注册

XMPP登记处维护着一个特性注册项,用于遵循 'http://jabber.org/protocol/disco#info' 名字空间的 <feature/> 元素的 'var' 属性的值;见<http://xmpp.org/registrar/disco-features.html>>。

过程

为了向注册项提交新值,注册人必须按如下格式定义一个XML片段,将它包含在相关的XMPP扩展协议中,或者发到<registrar@xmpp.org>:

<feature var='name of feature or namespace'>
  <desc>a natural-language description of the feature</desc>
  <doc>the document (e.g., XEP) in which this feature is specified</doc>
</feature>

注册人可一次注册一个以上的特性,每个特性包含在独立的<feature>元素中。


知名节点

一个正在使用的协议可以指定一个或以上的服务发现节点,这些节点在协议上下文中有特殊的、恰当定义的含义。为了全局性地跨所有Jabber协议保留这些节点名字,XMPP登记处在<[2]>维护着一个知名服务发现节点的注册。

过程

为了向登记处提交新值,注册人必须按如下格式定义一个XML片段,然后将它包含在相关的XMPP扩展协议中,或者发邮件到<registrar@xmpp.org>:

<node>
  <name>the name of the node</name>
  <desc>a natural-language description of the node</desc>
  <doc>the document (e.g., XEP) in which this node is specified</doc>
</node>

注册人可以一次注册一个以上的节点,每个节点包含在单独的<node/>元素中。

URI查询类型

作为由XMPP URI Query Components 17授权的机构,XMPP登记处维护着一个用于 XMPP URIs 的查询和键-值对的注册项(见<[3]>)。

在这里定义了用于服务发现交互的查询类型“disco”,它有三个键:(1)"node"(查询的可选节点),(2)"request"(带“info”值来取回服务发现信息,带“items”值取回服务发现条目),(3)“type”(带值“get”用于IQ取值,“set”用于IQ设置)。

例25. 服务发现信息请求:IRI/URI

xmpp:romeo@montague.net?disco;type=get;request=info

例26. 服务发现信息请求:结果节

<iq to='romeo@montague.net' type='get'>
  <query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>

例27. 服务发现条目请求: IRI/URI

xmpp:romeo@montague.net?disco;type=get;request=items

例28. 服务发现条目请求:结果节

<iq to='romeo@montague.net' type='get'>
  <query xmlns='http://jabber.org/protocol/disco#items'/>
</iq>

下列提交注册"disco"查询类型。

<querytype>
  <name>disco</name>
  <proto>http://jabber.org/protocol/disco</proto>
  <desc>enables interaction for the purpose of service discovery</desc>
  <doc>XEP-0030</doc>
  <keys>
    <key>
      <name>node</name>
      <desc>the (optional) service discovery node</desc>
    </key>
    <key>
      <name>request</name>
      <desc>the service discovery request type</desc>
      <values>
        <value>
	  <name>info</name>
          <desc>a service discovery information (disco#info) request</desc>
        </value>
        <value>
	  <name>items</name>
          <desc>a service discovery items (disco#items) request</desc>
        </value>
      </values>
    </key>
    <key>
      <name>type</name>
      <desc>the IQ type</desc>
      <values>
        <value>
	  <name>get</name>
          <desc>an IQ get</desc>
        </value>
        <value>
	  <name>set</name>
          <desc>an IQ set (disco publish)</desc>
        </value>
      </values>
    </key>
  </keys>
</querytype>

XML架构

disco#info

<?xml version='1.0' encoding='UTF-8' ?>
 
<xs:schema
    xmlns:xs='http://www.w3.org/2001/XMLSchema'
    targetNamespace='http://jabber.org/protocol/disco#info'
    xmlns='http://jabber.org/protocol/disco#info'
    elementFormDefault='qualified'>
 
  <xs:annotation>
    <xs:documentation>
      The protocol documented by this schema is defined in
      XEP-0030: http://www.xmpp.org/extensions/xep-0030.html
    </xs:documentation>
  </xs:annotation>
 
  <xs:element name='query'>
    <xs:complexType>
      <xs:sequence minOccurs='0'>
        <xs:element ref='identity' maxOccurs='unbounded'/>
        <xs:element ref='feature' maxOccurs='unbounded'/>
      </xs:sequence>
      <xs:attribute name='node' type='xs:string' use='optional'/>
    </xs:complexType>
  </xs:element>
 
  <xs:element name='identity'>
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base='empty'>
          <xs:attribute name='category' type='xs:string' use='required'/>
          <xs:attribute name='name' type='xs:string' use='optional'/>
          <xs:attribute name='type' type='xs:string' use='required'/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
 
  <xs:element name='feature'>
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base='empty'>
          <xs:attribute name='var' type='xs:string' use='required'/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
 
  <xs:simpleType name='empty'>
    <xs:restriction base='xs:string'>
      <xs:enumeration value=''/>
    </xs:restriction>
  </xs:simpleType>
 
</xs:schema>

disco#items

<?xml version='1.0' encoding='UTF-8' ?>
 
<xs:schema
    xmlns:xs='http://www.w3.org/2001/XMLSchema'
    targetNamespace='http://jabber.org/protocol/disco#items'
    xmlns='http://jabber.org/protocol/disco#items'
    elementFormDefault='qualified'>
 
  <xs:annotation>
    <xs:documentation>
      The protocol documented by this schema is defined in
      XEP-0030: http://www.xmpp.org/extensions/xep-0030.html
    </xs:documentation>
  </xs:annotation>
 
  <xs:element name='query'>
    <xs:complexType>
      <xs:sequence minOccurs='0'>
        <xs:element ref='item' minOccurs='0' maxOccurs='unbounded'/>
      </xs:sequence>
      <xs:attribute name='node' type='xs:string' use='optional'/>
    </xs:complexType>
  </xs:element>
 
  <xs:element name='item'>
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base='empty'>
          <xs:attribute name='action' use='optional'>
            <xs:simpleType>
              <xs:restriction base='xs:NCName'>
                <xs:enumeration value='remove'/>
                <xs:enumeration value='update'/>
              </xs:restriction>
            </xs:simpleType>
          </xs:attribute>
          <xs:attribute name='jid' type='xs:string' use='required'/>
          <xs:attribute name='name' type='xs:string' use='optional'/>
          <xs:attribute name='node' type='xs:string' use='optional'/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
 
  <xs:simpleType name='empty'>
    <xs:restriction base='xs:string'>
      <xs:enumeration value=''/>
    </xs:restriction>
  </xs:simpleType>
 
</xs:schema>

作者备注

Peter Millard, 本文的一个合著者,从版本0.1到版本2.2, 死于2006年4月26日.

附录

附录A:文档信息

B:作者信息

C:法律通告

D:XMPP相关

E:讨论地点

F:需求一致性

G:备注

H:版本历史

修订历史

本文档定义了XMPP协议扩展用于发现(1)Jabber实体的信息和(2)与这些实体相关的细节。

注意 此处定义的协议是XMPP标准基金会的最终标准,可作为稳定技术用于开发和部署。

文档信息

系列:XEP

序号:0030

发布者:XMPP标准基金会

状态:终结版

类型:标准跟踪

版本:2.3

最后更新:2007-02-15

批准机构:XMPP理事会

依赖标准:XMPP Core

替代标准:XEP-0011, XEP-0094

被替代标准:无

缩略名:disco

命名空间disco#info的XML方案(Schema)[4]

命名空间disco#items的XML方案(Schema)[5]

作者信息

法律通告

讨论地点

相关的XMPP

术语的一致性

个人工具