会话初始协议ISP技术要求-中国通信行业标准(2)

时间:2008-10-19 来源: 作者: 点击:
本地URI 必须设置为From 头字段的URI 值。 8.2 对话中的请求 对话在两个UA 之间建立之后,任一个UA 都可根据需要在对话中发起一个新的事务。在该事务中,发送请求消息的UA 作为UAC ,接收请求消息的UA 作为UAS 。这
  
本地URI 必须设置为From 头字段的URI 值。

8.2 对话中的请求
对话在两个UA 之间建立之后,任一个UA 都可根据需要在对话中发起一个新的事务。在该事务中,发送请求消息的UA 作为UAC ,接收请求消息的UA 作为UAS 。这种情况,UA 的角色可能和建立对话是的事务中的有所不同。
对话中的请求消息可包括Record-Route 和Contact 头字段。但是即使修改了远端目的URI ,这些请求仍不会改变对话的路由集。另外, 非目的刷新的请求消息不会修改对话的远端目的URI ,而目的刷新的请求消息则会修改它们。由INVITE 请求建立的对话,唯一的目的刷新请求定义为re-INVITE (具体参见本规范第14 章)。针对由其他方式建立的对话,有相应的目标刷新请求的扩展。
ACK 不是一个目标刷新请求消息。目标刷新请求消息仅是更新对话的远端目的URI ,而不是更新Record-Route 头字段的路由集,因为这样会引入与RFC2543 系统的兼容性问题。
1. 8.2.1 UAC 行为
2. 8.2.1.1 发起请求

一个对话中的请求消息由对话所保存的状态信息来构建。
请求消息的To 头字段的URI 必须设置成对话状态中的的远端URI;
请求消息的To 头字段的标签值设置为对话ID 的远端标签值;
请求消息的From 头字段的URI 必须设置为对话状态中的本地URI;
请求消息的From 头字段的标签值设置为对话ID 的本地标签;
如果本地或远端标签值为空,相应的To 或From 头字段中的标签参数必须头字段省略。

注:在后续的请求中使用初始请求的To 和From 头字段的URI 值,是为了保证RFC2543 的后向兼容性。在RFC2543 中采用URI 作为对话标识。本规范中仅用标签作为对话标识。本规范的后续的修订中,中间请求的To 和From URI 的强制映射关系将有可能不被采用。
本协议规定,请求消息中的Call-ID 必须设置为对话的Call-ID 。
对话内的中间请求必须包含CSeq 序列号,CSeq 序列号是一个随着CSeq 值依照各自方向严格单向按1 递增的值,因此, 如果本地序列号非空,Cseq 序列号值必须按1 递增, 同时必须放在CSeq 头字段内。如果本地序列号为空, 必须按照8.1.1.5 节的方法为其设置初始值。CSeq 头字段中的请求方法必须与请求消息中的方法一致。
UAC 采用远端目的和路由集来构建请求的Request-URI 和Route 头字段。
如果路由集为空,UAC 必须将Request-URI 置为远端目的URI 值,同时不必在请求消息中添加Route 头字段。
如果路由集不为空,同时路由集的第一个URI 中包含一个lr 参数(参见本规范19.1.1 节),UAC 也必须将Request-URI 置为远端目的URI 值,同时必须包含一个Route 头字段, 且按顺序包含路由集的值及所有参数。
如果路由集不为空,同时第一个URI 不包含lr 参数,UAC 必须置Request-URI 为路由集的第一个URI,且不允许去掉任何参数。同时UAC 必须添加一个Route 头字段,按顺序包含路由集剩余部分值及所有参数。UAC 必须将远端目的URI 置为Route 头字段的最后一个值。例如:
对端目的URI 为 sip:user@remoteua
路由集包含: <sip:proxy1>,<sip:proxy2>,<sip:proxy3;lr>,<sip:proxy4>
则:请求消息的Request-URI 和Route 头字段将被构建为下面格式:

METHOD sip:proxy1 Route: <sip:proxy2>,<sip:proxy3;lr>,<sip:proxy4>,<sip:user@remoteua> 如果路由设置的第一个URI 不包含lr 参数,代理表示不理解本规范所规定的路由机制,它就会依据RFC2543 中的规定,将Request-URI 用它收到的前向消息中的Route 头字段的第一个值来代替。对话过程中的任一个更新目的的请求消息应该包含一个Contact 头字段,如果无特殊情况需要更改, Contact 的URI 应该与该对话前面的请求消息中的URI 相同。如果"安全"标签为“TRUE”
,URI 就必须
是一个SIPS URI 。参见本规范12.2.2 节,目的更新请求消息的Contact 头字段会更新远端目的URI 。然而,非目的更新的请求消息不会影响此次对话的远端目的URI 。一旦请求消息被构建,服务器地址被确定,同时请求消息被发送,其处理同对话外的请求消息的
处理。参见本规范8.1.2 节中的处理流程,请求将会被发送到Route 头字段的最顶端所标明的地址,如果无Route 头字段的, 请求消息将被发送至Request-URI 标明的地址上。特殊情况下, 也允许将请求发送至一个可替换的地址上。
8.2.1.2 响应处理
UAC 从事务层收到对请求的响应消息。如果客户端事务层返回一个超时信息,对其的处理与408 响应相同,对话过程中,UAC 收到3xx 响应的处理与对话外响应的处理相同,具体规则参见本规范8.1.3.4 节。
但当UAC 进行位置选择时,它还会使用对话的路由集去构建请求消息的Route 头字段。
当收到对目的刷新请求消息的2xx 响应时,UAC 必须将对话的远端目的URI 设置为响应消息中存在的Contact 头字段的URI 值。如果响应为481(呼叫/事务不存在)或者408(请求超时),UAC 应该终止对话;在在对方无响应的时候UAC 也应终止对话,客户端事务处理将会通知TU 超时。
以INVITE 请求消息发起的对话,UAC 通过发送BYE 来终止对话。
8.2.2 UAS 行为
对话中发送的请求与其他任何请求一样是原子的,如果UAS 接收一个特定的请求,所有相关的状
态将会被迁移;如果请求被拒绝,将不会有状态迁移发生。仅有一些特殊的请求会影响状态迁移,比如INVITE 请求。UAS 会收到来自于事务层的请求消息。如果该请求To 头字段中存在标签,UAS 内核(UAS core)
将会计算与此请求相关的对话标识符, 同时将其与已有的对话进行比较。如果相匹配的话, 该请求即为一个对话内的请求。在这种情况下,UAS 采用与对话外请求消息处理规则相同的处理流程对其作出响应。具体规则参见本规范8.2 节。如果请求消息的To 头字段包含标签,但对话标识符与与已有的对话不匹配,则表示UAS 可能曾经重新启动过, 或者收到了发给其他的UAS 请求消息, 或者就是接收到一个完全被发错的请求。基于To 头字段标签值,UAS 可以接收或拒绝这个请求。接收请求,导致拆掉的对话得以延续。支持这种能力的UA 必须考虑到如选择单向递增的CSeq 序列号、路由集的重建、以及接收越界的RTP 时间信息与序列号等问题。
如果UAS 不愿重建此次对话而拒绝该请求,那么它必须回一个481(呼叫/事务不存在)的状态码给服务器端的事务。
UAS 可以接受不改变对话状态的请求消息,如OPTIONS 请求,对它的处理与在对话外的请求消息的处理相同。
如果对话的远端序列号为空,那它必须被设置为请求CSeq 头字段的序列号值;
如果远端序列号非空,但是请求中序列号值比远端序列号低,则认为请求次序颠倒,必须回500 (服务器内部出错)响应拒绝请求;
如果远端序列号非空, 同时请求消息中序列号值比远端序列号高,请求次序正常,且CSeq 序列号与远端序列号相比差值大于1,此时不认为出错,同时UAS 应该准备接收此请求并对其进行处理。UAS 必须设置远端序列号为请求消息的CSeq 头字段的序列号值。
当UAS 接收到一个目的更新请求,它必须用请求消息已有的Contact 头字段的URI 值替换对话的远端目的URI 头字段。
8.3 终止对话
初始对话的终止不依赖于发起请求的具体方法,只要收到一个非2xx 的终止响应即可将其终止。证实/确认对话(confirmed dialogs) 的终止机制与确切方法相关。本协议规定,BYE 方法终止一次会话(session), 同时终止一次与其相关的对话,具体内容参见本规范第15 章。
9 会话发起过程
9.1 概述
UA 通过向服务器发送INVITE 消息开始会话发起过程,该请求可能通过网络中间的服务器设备的
转发,最终到达UAS 。如果UAS 同意建立本次会话,则返回2XX 响应,如果不同意,则返回3XX 、
4XX 、5XX 、6XX 响应。在收到最终响应之前,UAS 也可以发送临时响应(1XX )来通知UAC 当前的
处理进展情况。
,由于该INVITE 请求可能被分支(Forking)UAC 可能会收到一个或多个2XX 响应或者一个非2XX
响应。UAC 收到最终响应消息之后需要向UAS 侧发送ACK 消息。
每一个2XX 响应都会创建一个Dialog, 因此如果UAC 收到多个2XX 响应, 那么每个2XX 都创建
一个Dialog ,这些Dialog 都属于同一个呼叫。
本节对会话建立过程的细节内容加以规定。
1. 9.2 UAC的处理过程
2. 9.2.1 创建初始INVITE 消息

由于初始的INVIT 消息是Dialog 之外的请求, 因此它的构造过程需要遵循通用的Dialog 外请求消
息构造过程(参见:8.1.1 节)。下面针对INVITE 消息的特殊情况进行说明:
必须包含Allow 头域,用于表示本次会话过程中UAC 支持的请求方法。
必须包含Supported 头域, 用于表示UAC 支持的扩展功能。
INVITE 消息中可以包含SDP 信息,这时Content-Type 头域应该是application/sdp 。
SIP 协议中规定关于媒体协商的过程是通过在消息中携带SDP 来完成的。关于SDP 协商的过程需要遵循如下规定:
INVITE 消息中携带SDP 请求,200 消息中返回SDP 响应。
200 消息中携带SDP 请求,ACK 消息中返回SDP 响应。
SDP 请求和响应这个协商过程,不能并行,只能当一次交互完成之后才能发起新的协商过程。
9.2.2 处理对INVITE 消息的响应
1XX 响应:在收到最终响应之前,UAC 可能收到零个、一个或者多个临时响应。
3XX 响应:一个3XX 响应中可以包含一个或者多个Contact 头域,这些头域代表被呼叫方的新的地址。UAC 可以根据具体的响应类型以及本地的业务策略来选择是否向这些地址发起新的会话请求。
4XX 、5XX 和6XX 响应:UAC 只能收到一个非2XX 响应。其中可以包含一个Contact 头域用于提供关于会话建立失败信息的查询地址。UAC 收到非2XX 响应, 即认为INVITE 事务已经完成, 向UAS 发送ACK 。
2XX 响应:由于INVITE 请求可能被分岔(Forking),因此UAC 可能收到多个2XX 响应。这些响应通过To 头域中的tag 参数相区别,每一个响应都代表一个独立的Dialog 。UAC 必须对每个2XX 都返回ACK 。
1. 9.3 UAS的处理过程
2. 9.3.1 处理INVITE 消息

正在处理:如果UAS 不能马上作出应答, 它可以发送相应的1XX 响应来通知UAC 当前的处理进展(例如:振铃,被前转等)。
重定向:如果UAS 希望将该请求重定向,它可以发送3XX 响应, 其中携带新的被叫地址。要求UAS 向新的地址发起呼叫。
拒绝:如果UAS 由于某种原因不能接受这个呼叫请求,它可以根据具体的原因,发送相应的4XX, 5XX,6XX 响应。
接受:UAS 返回2XX 响应,表示接受本次呼叫请求。这个响应消息同时也建立了一个Dialog 。
2XX 响应中必须包含Allow 头域,用于表示UAS 支持的请求方法。2XX 中必须包含Supported 头域,用于表示UAS 支持的扩展能力。
如果INVITE 请求中,包含SDP 请求,那么UAS 必须在2XX 返回一个SDP 应答。
UAS 必须重发2XX 直到接收到ACK 确认消息。重发间隔为T1,每次重发间隔加倍,直到达到T2。如果这时仍未收到ACK,UAS 必须结束本次会话, 向UAC 发送BYE 请求。
10 会话更改过程
24
10.1 概述
会话建议之后,任何一方都可以发起会话更改请求,修改会话的某些属性,例如:增加删除媒体流, 改变媒体发送接收地址等。这是通过在已经建立的Dialog 中发送一个新的INVITE 请求来完成的。我们称之为reINVITE 。
10.2 UAC的处理过程
reINVITE 过程所采用的SDP 协商过程与建立会话的INVITE 过程相同。会话中的任意一方可以通过在reINVITE 中携带一个新的SDP 请求来更新会话内容。
reINVITE 消息中的To、From 、Call-ID 、Cseq 和Request-URI 头域的生成方法采用通用的Dialog 中请求消息的生成规则相同(参见第12 章)
reINVITE 不会被分岔, 因此只可能收到一个最终响应。
reINVITE 过程不能重叠, 如果已经有一个reINVITE 事务正在执行,就不能发起新的reINVITE 事务。
对于reINVITE 事务的ACK 和2XX 响应的生成,与初始INVITE 过程相同。
10.3 UAS的处理过程
UAS 收到reINVITE 消息后,必须检查其中的SDP 是否更改,并对相应的会话参数做出调整。如果新的媒体描述不可接受,UAS 可以返回488 拒绝响应。
如果UAS 返回了2XX 响应,但是没有收到ACK ,它必须发送BYE 来结束本次会话。
11 会话结束过程
11.1 概述
会话的中止可以通过对INVITE 请求返回拒绝相应,对已建立的会话发送BYE 请求等方式来完成。
1. 11.2 通过BYE 请求结束会话
2. 11.2.1 UAC 的处理过程

会话中的任意一方可以通过发送BYE 请求来结束已经建立的会话。BYE 请求的生成与通用的
Dialog 中请求消息的生成规则相同( 参见第12 章)。
BYE 请求对应一个新的事务。UAC 发送BYE 请求之后即认为本次会话已经结束了。
11.2.2 UAS 的处理过程
UAS 收到BYE 请求之后, 需要查询匹配的会话。如果找不到则返回481 响应。如果找到对应的会
话,UAS 必须结束该会话。然后对BYE 返回2XX 响应。对于正在处理的请求消息,UAS 返回487 响
应。
12 代理服务器(代理服务器)的行为
12.1 概述
代理服务器是将请求消息路由到UAS 以及将响应消息路由到UAC 的实体。一个请求消息在到达UAS 之前可能要经过若干个代理服务器的转发,每个代理服务器都要进行路由决策,并在将请求消息转发到下一个实体之前对其进行修改。响应消息将遍历请求消息所经的那些服务器,但顺序却完全相反。
代理服务器是一个逻辑SIP 实体。当一个请求消息到来时,一个能做为代理服务器的SIP 实体首先决定是否需要由自己来应答这个请求,例如请求消息中可能有格式错误,或者在执行代理功能之前需要先获得客户端的鉴权证书等,而该实体亦可用任何适当的错误码来响应。SIP 实体直接应答一个请求时,它承担的角色就是UAS ,因而必须按8.2 节所述来执行相应的处理功能。
对每个新请求, 代理服务器既可以在有状态模式下工作, 也可以在无状态模式下工作。当以无状态模式工作时,代理服务器只是作为一个简单的消息转发实体,它根据请求消息来做转发目的地和路由决策,然后把请求转发到下游的某个实体;对于响应消息,则只简单地将其往上游方向转发。一旦消息转发完毕,无状态代理服务器将丢弃所有与此消息相关的信息。有状态代理服务器会记住它所收到的每个请求的信息, 如事务状态,以及作为某一请求的处理结果而发送的任何请求的信息。这些信息将影响它对后续的、与先前接收的某一请求相关的消息的处理。有状态代理服务器可能选择“分叉”(fork )转发一个请求,即将一个请求向多个目的地路由。任何被转发到多个地点的请求都必须在有状态模式下处理。
有些情况下,代理服务器可能采用有状态的传输方式(比如TCP )来转发请求,这时不必保留事务状态。例如,代理服务器可能在不保留事务状态的情况下将一个请求从一个TCP 连接转发到另一个TCP 连接,只要它在请求消息中放置足够的信息,使它能把对应的响应消息沿着接收请求所用的那条TCP 连接往回转发。当请求消息在不同类型的传输方式之间转发时, 代理服务器必须采用有状态模式, 代理服务器的事务用户必须保证消息的可靠传输。
一个有状态的代理服务器可能在请求处理过程中的任何时侯转到无状态工作模式下,只要它之前无任何禁止这种转换的动作, 如分叉代理或产生了100 响应等动作。在做这样的转换时, 所有的事务状态信息都将被丢掉。代理服务器不应主动发起CANCEL 请求。
12.2 有状态代理服务器
一个工作在有状态模式下的代理服务器就是一个SIP 事务处理引擎。有状态代理服务器的行为模型可根据客户端事务和服务器端事务的有关定义而建立。一个有状态代理服务器包含一个服务器端事务, 一个或多个客户端事务,以及将它们关联起来的一个被称为代理服务器核心的( 参见图3) 高层处理模块。从外部收到的请求由服务器端事务来处理。处理之后的请求送给代理服务器核心层, 核心层决定将请求路由到何处,选择一个或多个下一跳位置。发往每个下一跳位置的请求消息都由一个它自己的客户端事务进行处理。核心层从客户端事务收集响应,并依据这些响应来向服务器端事务发送响应。
C
T ST: 服务器端事务
S T 代理服务器核心 C T CT: 客户端事务

C
T

图3 有状态代理服务器模型
有状态代理服务器为每个收到的请求创建一个新的服务器端事务。任何重传的请求由服务器端事务的处理过程参见本规范第17 章。当立即回送临时性响应消息(例如100 Trying )到服务器端事务时,代理服务器核心必须作为UAS 进行处理,参见本规范8.2.6 节。有状态代理服务器不应当对非INVITE 请求产生100(Trying )响应。
本节描述的是代理服务器行为的模型,不是代理服务器软件的模型。具体实现时可以采用任何方法来完成该模型定义的外部行为。
对所有新请求,包括任何方法未知的请求,一个执行代理功能的实体必须:
1. 1. 确认请求的有效性(参见本规范16.3 节)
2. 2. 预处理路由信息(参见本规范16.4 节)
3. 3. 确定请求发送的目的地(参见本规范16.5 节)
4. 4. 将请求向每个目的地转发(参见本规范16.6 节)
5. 5. 处理所有的响应消息(参见本规范16.7 节)
6. 12.3 确认请求的有效性

一个实体在进行代理服务之前必须确认请求的有效性。一个有效的请求消息必须通过如下的检查:
1. 1. 合理的语法
2. 2. URI 方案
3. 3. 最大转发数
4. 4. 回路检测(可选)

1. 5. Proxy-Require
2. 6. Proxy-Authorization

如果任何一项检查失败,代理服务器必须象UAS 那样处理(参见本规范8.2 节)并用一个错误码来应答。
本协议规定, 代理服务器不要求进行请求合并的检测,并且不应把合并的请求当作错误处理。接收请求的端点将会解决请求合并问题,处理方式参见本规范8.2.2.2 节。
12.3.1 消息的语法检查
请求消息的语法构造必须正确,这样服务器端事务才可以处理该请求。任何与请求的有效性相关的消息内容,或者与请求的转发相关的消息内容, 其构造必须正确。对任何其它的消息内容,在消息转发时应忽略其语法构造是否正确并保持原样。例如,SIP 实体不会因为一个格式错误的Date 头字段而拒绝一个请求。同样,代理服务器在转发一个请求之前也不会删除一个格式错误的Date 头字段。
本规范是可扩展的。将来的扩展可能定义新的请求方法和新的消息头字段。一个实体不可以因为某个请求包含了它不认识的请求方法或者消息头字段而拒绝代理该请求。
12.3.2 URI 编码格式检查
如果一个请求的Request-URI 使用了代理服务器不能理解的URI 方案,代理服务器应当用一个416 ( 不支持的URI 方案)响应拒绝该请求。
12.3.3 最大转发数检查
Max-Forwards 头字段用来限制一个SIP 请求消息所能经过的实体的最大数目。
如果请求消息中不包含Max-Forwards 头字段,本项检查通过。
如果请求消息中包含Max-Forwards 头字段,并且其值大于零,本项检查通过。
如果请求消息中包含Max-Forwards 头字段,并且其值等于零,那么SIP 实体不可以转发此请求。如果请求方法是OPTIONS ,那么SIP 实体可作为消息的最终接收者, 并按照第11 章的响应过程进行响应;否则,SIP 实体必须返回一个483(跳数过多)响应。
12.3.4 路由循环检测(可选)
SIP 实体在转发一个请求前可能进行转发循环的检测。如果SIP 实体收到的某个请求中包含了一个Via 头字段,其sent-by 参数值与该实体作为代理服务器时在以前的请求消息中加入的值相同,则检测到该请求已经被该实体转发过。这种情况可能是请求消息被循环路由,或者是合法地”螺旋路由”地经过这个SIP 实体。循环路由检测规则参见本规范16.6 节,SIP 实体可以根据该规则的第8 步来计算该消息的branch 参数,并将结果与上述Via 头字段中的branch 参数进行比较。如果两个参数相匹配,那么请求消息被循环路由;如果它们不同,那么请求消息被“ 螺旋路由”, 且处理继续。如果检测到路由循环, SIP 实体可能返回一个482(检测到路由循环)响应。
12.3.5 Proxy-Require 检查
本协议的扩展以后可能会引入一些要求代理服务器所具有的特殊的处理特性。终端在使用这些新特性的请求消息中加入一个Proxy-Require 头字段,代理服务器如果不理解指定特性将不能进行请求消息的处理。
如果请求消息中包含了一个Proxy-Require 头字段, 其中列出的某项或某几项扩展能力选项标签不能为SIP 实体所理解,SIP 实体必须返回一个420(错误的扩展)响应。该响应中必须包含一个Unsupported 头字段(参见20.40 节), 其中列出那些不能被理解的选项标签。
12.3.6 Proxy-Authorization 检查
如果SIP 实体在转发一个请求之前需要鉴权证书,则需要先进行消息检查。关于消息检查及检查失败后SIP 实体如何处理参见本规范22.3 节。
12.4 路由信息预处理
代理服务器必须检查请求消息的Request-URI 。如果请求消息的Request-URI 是它以前插在Record-Route 头字段中的值,代理服务器则必须用Route 头字段的最后一个值替换Request-URI ,并且从Route 头字段中删除该值。在这之后,代理服务器必须按修改过的请求来进行处理。
上述情况只有当发送请求到代理服务器( 可能是一个端点)的SIP 实体为一严格路由器(代理服务器)时才会发生。为了获得与这类实体的后向兼容性,此时对接收消息的修改是必要的。这也使得遵从本规范的实体能够在通过严格路由的代理服务器时保留Request-URI (参见12.2.1.1 节)

并不要求代理服务器为了检测它以前插入到Record-Route 头字段中的那些URI 而保留状态信息。代理服务器只需要在那些URI 中加入足够的信息,使其在以后出现时能够被识别出来就行了。
如果Request-URI 中包含maddr 参数,代理服务器必须检查其值是否在自己所负责的地址集或主域集中。如果在其中,且对应的请求消息是通过Request-URI 中指定或缺省的端口和传输方式接收的, 代理服务器必须去掉maddr 参数和所有非缺省的端口及传输方式参数,并按照没有这些参数的请求消息来进行处理。
代理服务器收到的某个请求消息中可能有与该服务器相匹配的maddr 参数,但接收该消息时所用的端口和传输方式却与Request-URI 中指定的不同。这样的请求需要被转发到使用Request-URI 中指定的端口和传输方式的代理服务器。
如果请求消息中Route 头字段的第一个值指示的就是本代理服务器,那么它必须将该值从请求消息中删掉。
12.5 确定请求的发送目标
代理服务器计算请求消息的发送目的地。发送的目标地址集可以由请求消息的内容预先决定,也可以是从某个抽象的定位服务获得。地址集中的每个目的地都被表示成一个URI 。
如果请求的Request-URI 中包含一个maddr 参数,Request-URI 必须被作为唯一的目标URI 放在目标地址集中,代理服务器对其处理的过程参见本规范16.6 节。
如果Request-URI 所指示的域不是代理服务器所负责的域,Request-URI 必须被作为唯一的发送目的地放进目标地址集中,然后该实体必须对该消息进行转发,具体过程参见本规范16.6 节。
许多情况下代理服务器都可能收到Request-URI 所指主域不归自己负责的请求消息。比如处理外拨呼叫的防火墙代理服务器(HTTP 代理服务器处理外发请求的方式)就是这种情况可能发生的一个例
, 子。
如果目标地址集不能通过上述的方法确定, 当前SIP 实体则负责Request-URI 中的主域,并且该实体可以用任何机制来决定向何处转发请求。不论是哪种机制,其模型都可以化为访问一个抽象定位服务
29
的过程。可能的机制有:从SIP 注册服务器创建的某个定位服务获得信息;读取数据库;查询一个presence 服务器; 使用其它协议;或者执行一个简单的算法对Request-URI 进行替换, 等等。当访问一个由注册服务器构建的定位服务时,必须首先按10.3 节所述对Request-URI 进行规范化,然后才能把它作为查询索引。这些机制的输出结果被用于构造目标地址集。
如果Request-URI 没有提供足够的信息使得代理服务器能够决定目标地址集,代理服务器应当返回一个485 (不明确) 响应。响应中应当包含一个Contact 头字段, 其中列出了可供尝试的新地址的URI 。例如,如果某代理服务器的定位服务中有多个John Smith ,则一个构造为sip:John.Smith@company.com 的INVITE 请求对该代理服务器是不明确的。有关内容参见本规范21.4.23 节。
任何在请求消息中,或者与请求消息相关的,或者实体的当前环境相关的信息都可能用于目标地址集的构建。例如, 根据某些头字段和消息体是否出现及其内容如何, 或请求消息到达的时间、或接收请求所用的接口、或以前请求消息的失败情况, 甚至实体当前的可用性级别等等,可能会构造出不同的目标地址集。
通过上述定位服务确定了可能的目标地址之后,它们的URI 就被加入到目标地址集中。目标地址只能被放置到目标地址集一次。根据URI 相等的定义,一个目标URI 已经在目标地址集中出现,那么它就不能被再次加入该地址集。
如果原请求的Request-URI 所指示的资源位置不在本代理服务器负责的范围之内,代理服务器不得在目标地址集中加入其它地址。
代理服务器只有在请求的Request-URI 是由自己所负责的情况下才能在消息转发时改变这个URI。
, 如果代理服务器对某个Request-URI 不负责,它将不能按如下所述进行3xx 类响应或416 响应的递归处理。
如果代理服务器负责原始请求的Request-URI 所指定的资源,则在请求转发开始之后它可能继续向目标地址集加入地址。它可以使用处理过程中获得的任何信息来决定新的目标地址。比如, 代理服务器可以将一个重定向响应(3xx 类响应)中获得的contact URI 并入目标地址集中。如果代理服务器在构造目标地址集时使用了动态信息源(比如查询一个SIP 注册服务器), 它应当在请求处理过程中监视所用信息源的变化。如果有新的可用地址, 那就应当将其加入目标地址集中。同样的地址决不能被多次加入。
允许一个URI 只被加入到目标地址集中一次可减少不必要的网络流量。
在将重定向响应中的contact 地址合并到目标地址集中时还可以防止无限递归的出现。
例如,“无操作”也是一个简单的定位服务,此时目标URI 就等于接收请求的Request-URI 。请求消息将被发送到特定的下一跳代理服务器以进一步处理。参见16.6 节第6 条的请求转发处理,用SIP 或SIPS URI 表示的下一跳地址标识被作为Route 头字段最顶端的值插入到请求消息中。
如果在Request-URI 中指定的某个资源不存在,代理服务器必须返回一个404(找不到)响应。
按上述所有的方法处理之后,目标地址集仍然为空, 代理服务器就必须返回一个错误响应, 并且应当是480 (临时不可用)响应。
12.6 请求的转发
只要目标地址集非空,代理服务器就可以开始转发请求。有状态代理服务器可以按任何次序来处理目标地址集。代理服务器可以对多个目标地址采用串行处理的办法,使得一个客户端事务完成之后才开始下一个; 它也可以同时开始所有的目标地址处理, 即并行处理相应的所有客户端事务; 它还可以将目标地址集任意分组,组与组之间串行处理,组内的目标地址并行处理。
一种常用的排序机制是利用目标地址的qvalue 参数,这些参数来自Contact 头字段。目标地址按qvalue 值的大小从最高往最低处理,qvalue 值相同的可能会同时处理。
有状态代理服务器必须有目标地址集的维护机制,这样当收到各个转发请求的响应后就能把它们与原始请求联系起来。本模型中,这是“响应上下文”的机制(response context),它由代理服务器层在转发第一个请求之前创建。
对每个目标地址,代理服务器按如下步骤转发请求:
1. 1. 复制收到的原始请求
2. 2. 更新Request-URI
3. 3. 更新Max-Forwards 头字段
4. 4. 添加一个Record-Route 头字段值(可选)
5. 5. 添加其它头字段(可选)
6. 6. 路由信息后处理
7. 7. 确定下一跳的地址、端口和传输方式
8. 8. 添加一个Via 头字段值
9. 9. 必要时加入一个Content-Length 头字段
10. 10. 转发新请求
11. 11. 设置定时器C

12.6.1 请求消息复制
代理服务器首先对收到的请求做一份拷贝。拷贝最初必须包含所收到的请求的所有头字段。在下面描述的处理过程中未提及的字段也不能删除。拷贝中各个头字段的顺序应当与所收到的请求保持一致。代理服务器决不能将头字段名相同的头字段值重新排序。代理服务器不能添加、修改、或删除消息体。
具体实现不需要进行真正的消息复制。其基本要求是对每个下一跳的处理都要从同一个请求消息开始。
12.6.2 Request-URI
拷贝请求消息的起始行的Request-URI 必须被替换为当前目标地址的URI 。如果该URI 中包含任何在Request-URI 中不允许出现的参数,这些参数必须被删掉。
代理服务器通过这种机制把一个请求消息路由到它的目的地。
有些情况下, 所收到的请求消息的Request-URI 没有经过任何修改就被放到了目标地址集中。对这样的目标地址,上述的替换实际上是一种”无操作”行为。
12.6.3 Max-Forwards 字段
如果消息拷贝中包含一个Max-Forwards 头字段,代理服务器必须将其值减一。
如果消息拷贝中不含Max-Forwards 头字段,代理服务器必须加入该字段,其值应当为70。现有的某些UA 在请求消息中不提供Max-Forwards 头字段。
12.6.4 Record-Route
如果代理服务器希望自己留在后续请求消息的传输路径上,而这些后续的请求消息属于由当前请求创建的一个对话, 那么即使请求中已经出现了Route 头字段,代理服务器也必须在请求消息的拷贝中任何已有的Record-Route 头字段值之前插入一个Record-Route 头字段值。
建立对话的请求消息中可能包含预置的Route 头字段。
如果当前请求已经属于某一个对话,那么代理服务器要想留在该对话中后续请求消息的传输路径上,它应当在请求中插入一个Record-Route 头字段。参见本规范第12 章,正常情况下,这些Record-Route 头字段将不会对终端所用的路由集产生任何影响。
对于已经属于某个对话的请求消息,即使代理服务器不在其中插入Record-Route 头字段值,它也能留在后续请求的传输路径上。然而,在端点发生了错误后重建对话时, 这个代理服务器将被从以前的传输路径上移除。
代理服务器可能在任何请求中插入Record-Route 头字段值。如果请求不是用来发起对话,那么终端将会忽略这个Record-Route 头字段值。关于终端如何利用Record-Route 头字段来构造Route 头字段参见本规范第12 章。
请求传输路径上的每个代理服务器都独立决定是否在请求消息中添加一个Record-Route 头字段值。请求消息中已经出现的Record-Route 头字段,并不要求代理服务器一定得添加新值。
在Record-Route 头字段中放置的URI 必须是一个SIP URI 或SIPS URI 。这个URI 必须包含一个lr 参数( 参见19.1.1 节)。对请求转发的每个目的地,这个URI 都可能不同。该URI 不应包含传输方式参数,除非代理服务器明确相邻的下游实体支持该参数所示的传输方式, 同时该下游实体将出现在后续请求的传输路径上。
当前代理服务器提供的URI 将被其它SIP 实体用于路由决策。通常来说,代理服务器无法知道其它实体的能力,所以它必须满足SIP 实现的必要条件:支持SIP URI 以及TCP 或UDP 传输方式。应用RFC3263 中所述的服务器定位过程对Record-Route 头字段中加入的URI 进行解析,其结果必须是插入该URI 的SIP 实体, 这样使得后续的请求能够到达同一个SIP 实体。如果请求消息的Request-URI 包含了一个SIPS URI, 或者最顶端的Route 头字段值在路由信息后处理之后包含了一个SIPS URI ,那么代理服务器在Record-Route 头字段中加入的URI 也必须是SIPS URI 。另外,如果请求消息不是通过TLS 方式接收的,那么代理服务器必须插入一个Record-Route 头字段。类似地,如果请求消息是通过TLS 接收的,但转发的请求消息中Request-URI 或最顶端Route 头字段值在路由信息后处理之后不是SIPS URI,那么代理服务器必须在转发的请求消息中插入一个非SIPS URI 的Record-Route 头字段。
一个处于某一安全范围内的代理服务器,在对话当中必须保留在该安全范围内。
当插在Record-Route 头字段中的URI 通过响应消息又被传回来时,如果代理服务器需要重写该URI,则该URI 必须清晰明确以便被准确查找(请求消息可能通过螺旋路由多次经过一个代理服务器, 导致代理服务器加入多个Record-Route 头字段值)。
代理服务器可能在Record-Route 头字段值中包含一些参数。这些参数会在请求的某些响应消息中被送回,如INVITE 请求的200(OK)响应。如果想在消息中而不是代理服务器中保存一些状态信息, 便可以使用这些参数。
如果不论对话是什么类型,代理服务器都需要留在它的消息传输路径上,代理服务器则应当在每个请求消息中都加入一个Record-Route 头字段值,即使它不认识请求的方法。不认识的请求也可能会建立或属于某个对话。
代理服务器加到某一请求的Record-Route 头字段中的URI 只在相应的对话生命期中有效,这个对话由该请求所在的事务创建。例如, 一个保持对话状态的代理服务器在对话终止之后, 可能会拒绝接收Request-URI 的某个URI 的后续请求消息, 因为该URI 的有效期已过。当然, 一个没有对话状态的代理服务器无从得知对话是否终止,但它可以在插入的URI 中添加足够的信息, 再将其同后续请求的对话标识相比较,其对话标识与信息不相匹配的请求将有可能被拒绝。终端不可以在某个对话外使用在该对话中得到的Record-Route 头字段的URI 。关于终端如何使用Record-Route 头字段参见本规范第13 章。
Record-Route 操作可能是某些特定业务所要求的,这些业务需要代理服务器察看一个对话中的所有消息。但是, 这种操作降低了系统的处理速度和系统的可扩展性,所以只有在特定的业务要求时, 代理服务器才应进行Record-Route 操作。
Record-Route 的操作是处理能够发起一个对话的SIP 请求消息。本规范所涉及这类请求只有INVITE 。
12.6.5 其它消息头字段的加入
代理服务器可以在请求消息的拷贝中加入其它合适的头字段。
12.6.6 路由信息后处理
代理服务器可以有本地策略,强制某个请求消息在被发到目的地之前必须经过某些特定的代理服务器。但它必须保证这些特定的代理服务器都是松散路由器。通常来说, 只有同一个管理域中的代理服务器才能确切知道彼此是否为松散路由器。这些特定的代理服务器由一系列的URI 表示,每个URI 都含有lr 参数。这些URI 必须被加到请求消息拷贝中的Route 头字段的任何已有的值之前。如果请求拷贝中没有出现Route 头字段,那么需要加入一个新的Route 头字段,该字段中包含那些必经的代理服务器的URI 。
如果代理服务器有一个本地策略,要求请求消息必须经过某一个特定的代理服务器,可以简单的直接按该代理服务器的地址、所用的端口以及传输方式把请求发送给它。但如果请求消息中有Route 头字段,那么代理服务器就不能采用这种方法,除非它能确定那个下一跳必经代理服务器是个松散路由器。采用上述在请求消息中插入Route 头字段值的办法, 有着更好的鲁棒性、灵活性、通用性和操作上一致性,因此本规范建议,应优先考虑这种方式实现。此外,如果请求的Request-URI 中包含的是个SIPS URI, 那么在与那个必经代理服务器通信时必须使用TLS 。
如果请求消息的拷贝中包含Route 头字段,代理服务器必须检查其第一个值所含的URI 。如果那个URI 没有包含lr 参数,那么代理服务器必须按如下方式修改请求消息的拷贝:
--首先,代理服务器必须将Request-URI 移至Route 头字段中,并将其作为Route 头字段的最后一个值。--然后, 代理服务器必须把Route 头字段中的第一个值作为Request-URI,并将其从Route 头字段中
删除
在请求消息在通过严格路由实体时,需要一种机制来保证Request-URI 所携带的信息不被丢失,将Request-URI 附在Route 头字段之后则是这种机制的一部分。将Route 头字段的第一个值”弹出”到Request-URI 中使得一个严格路由实体在接收请求时可以得到它所希望的消息格式(请求消息的Request-URI 即为自己的URI ,请求消息需要访问的下一个位置由Route 头字段的第一个值给出)。
12.6.7 确定下一跳的地址、端口、传输方式
代理服务器可以有一个本地策略,即将请求消息发往一个特定的IP 地址, 使用特定的端口和传输方式,而与请求消息的Route 头字段和Request-URI 值无关。如果代理服务器不能确定对应于特定地址、端口、以及传输方式的服务器是一个松散路由器, 那么它就不能使用这种策略。本规范不建议采用这种机制发送请求消息到特定下一跳位置。
如果没有这种本地策略,代理服务器按如下方式应用RFC3263 中所列的解析过程来决定向何处转发请求: 如果象12.6.6 中描述的那样,代理服务器为将请求消息发送到一个严格路由实体而对其作了修改,那么它必须将相应的解析过程应用于解析请求消息的Request-URI 。否则,代理服务器必须将其用于解析Route 头字段的第一个值,如果无Route 头字段,则仍然用于解析Request-URI 。解析过程将产生一个有序的结果集,其中的每个元素是一个由IP 地址、端口和传输方式构成的三元组。不管使用哪个URI 作为RFC3263 中解析过程的输入,如果请求消息的Request-URI 是一个SIPS URI, 代理服务器都要把输入URI 当作SIPS URI 来处理。
如RFC3263 所述, 代理服务器必须尝试按解析结果集的第一个三元组发送请求消息,然后依序继续进行,直到发送成功为止。
在尝试发送的每个三元组,代理服务器必须对消息作适当的修改,并用一个新的客户端事务来发送请求。参见12.6.8 到12.6.10 。
既然每次发送尝试都用一个新的客户端事务,即代表一个新的对话分支(branch)。因此,12.6.8 中插入的Via 头字段中的branch 参数对每次发送尝试都必须不同。
如果客户端事务报告发送失败,或其状态机超时,代理服务器将按次序尝试解析结果集中的下一条地址。如果该集合中的地址全部试完, 则说明请求消息无法转发到目标地址集中当前URI 所指的实体。此时代理服务器不需要在响应上下文中放置任何信息,就如同该实体返回了一个408( 请求超时)响应。
12.6.8 添加一个Via 头字段值
代理服务器必须在请求消息的拷贝中已有Via 头字段值之前插入一个新值。该值的构造同样遵循
8.1.1.7 节所述的规则。这意味着代理服务器将计算它自己的branch 参数,该参数为全局唯一的,来标识相应的对话分支,并包含必需的magic cookie 。这也意味着对于循环路由或螺旋路由多次经过某个代理服务器的请求消息而言,每次经过该代理服务器时它得到的branch 参数是不同的。
如果代理服务器选择进行路由循环检测,它们用于构造branch 参数的值将有更多的限制。这些代理服务器创建的branch 参数应当可以分为两部分:第一部分必须满足上述8.1.1.7 节的要求;第二部分用于执行路由循环检测以及区分循环路由和螺旋路由。
路由循环检测的方法是:当一个请求消息返回到代理服务器时,如果那些对请求消息处理有影响的头字段都没有改变,则认为出现了路由循环。branch 参数中用于路由检测部分的值应当体现出所有这些头字段的信息(包括任何Route,Proxy-Require 以及Proxy-Authorization 头字段)。这就保证当请求消息被再次路由到代理服务器时,如果这些头字段中有一个发生改变,就可认为请求消息发生了螺旋路由而非循环路由,参见本规范16.3 节。创建路由检测部分的值有一个常用方法,即计算一个包括所有相关头字段相关信息的加密哈希表(hash),包括To tag,From tag,Call-ID 头字段, 转换前的原始接收请求的Request-URI, 最顶端Via 头字段,Cseq 头字段的序号值,以及任何可能出现的Proxy-Require 头字段和Proxy-Authorization 头字段。哈希表的产生算法依赖于具体的实现, 比较常用的是用十六进制表示的MD5 算法(RFC1321 ,附录35)。
如果代理服务器要检测循环路由,那么它提供的branch 参数必须依赖于所有对请求处理有影响的信息, 包括原始接收请求的Request-URI 以及任何影响请求消息的接受或路由的头字段。这样作对区分发生路由循环的请求和返回到当前服务器之前路由参数发生改变的请求时是必需的。
计算branch 参数时不能包括请求的方法。对于非2xx 响应的CANCEL 和ACK 请求与对应的被取消或被确认的请求必须有相同的branch 参数值。在处理这些请求的服务器上,branch 参数用于将这些请求相互关联起来。
12.6.9 必要时加入Content-Length 头字段
如果要采用流传输协议将请求消息发往下一跳, 请求消息中没有Content-Length 头字段,那么代理服务器必须在其中插入一个Content-Length 头字段,该头字段值等于消息体的长度(参见本规范20.14 节)。
12.6.10 请求消息转发
有状态代理服务器必须为转发请求创建一个新的客户端事务( 参见本规范17.1 节), 并指示这个事务使用由12.6.7 中所指定的方法确定请求发送的地址、端口和传输方式。
12.6.11 设置定时器C
事务用户使用了一个定时器C, 用来处理无法得到最终响应的INVITE 请求。当INVITE 请求被代理转发时,每个客户端事务都必须设置这个定时器。定时器C 的值必须大于3 分钟。关于收到临时响应后如何更新这个定时器参见本规范第16.7 节,关于定时器超时后如何处理参见本规范第16.8 节。
12.7 响应的处理
当一个SIP 实体收到响应时,首先它会查找与响应消息相匹配的客户端事务。如果没有找到,那么该实体必须作为一个无状态代理服务器( 参见下文中有关描述) 来处理这个响应( 即使它是一个信息性响应)。如果事务相匹配, 那么这个响应由所匹配的事务来处理。
转发找不到匹配事务(或更一般地,相关请求发送的任何信息)的响应消息提高了系统的鲁棒性。特别是可以保证INVITE 请求“迟到”的2xx 响应能被正确转发。
客户端事务把响应消息传给代理服务层之后,必须执行下述处理:
1. 1. 查找正确的响应上下文
2. 2. 对临时响应更新定时器C
3. 3. 删除最顶端Via 头字段值
4. 4. 把响应消息加到响应上下文中
5. 5. 检查响应是否需要立即转发
6. 6. 需要时,从响应上下文中选取最优的最终响应

如果同响应上下文相关的所有客户端事务都已终止,但还没有送出最终响应,代理服务器必须从它已有的响应中选取一个”最优的”转发出去。
对每个要转发的响应消息都必须作下述处理。每个请求都会至少有一个临时响应和一个最终响应将要转发。
1. 7. 必要时汇聚所有的authorization 头字段值
2. 8. 重写Record-Route 头字段值(可选)
3. 9. 转发响应消息
4. 10. 生成任何必要的CANCEL 请求
5. 12.7.1 查找响应上下文

参见本规范第16.6 节,代理服务器使用关键字查找与响应匹配的响应上下文,该上下文是它在转发原始请求之前创建的。接下来的处理都在这个响应上下文中进行。
12.7.2 对临时响应更新定时器C
对于INVITE 事务, 如果响应消息是一个响应码在101 到199 之间的临时响应,代理服务器必须重置相应客户端事务的定时器C。定时器C 可以被设为不同的值,但必须大于3 分钟。
12.7.3 Via 头字段
代理服务器必须从响应消息中将最顶端的Via 头字段值删除。
删除之后,响应消息中无Via 头字段值, 则该响应消息就是发给当前实体的, 不能再被转发。然后实体按照UAC 的方式进行处理,参见本规范8.1.3 节。如 6.7.10 节所述,这种情况在实体生成CANCEL 请求时就会发生。
12.7.4 将响应消息加入响应上下文
收到的最终响应被存在响应上下文中,直到与这个上下文相关的服务器端事务生成了一个最终响应。当前加入的响应可能是“最优”最终响应之一, 这个“ 最优”最终响应将由相关的服务器端事务返回。即使当前加入的响应不是最优的,它所携带的信息也可能用于构造“最优”响应。
如果代理服务器要对3xx 响应中的任何contact 地址进行递归处理,从而需要将这些地址加入到目标地址集中。在响应加入到响应上下文之前代理服务器必须把这些地址从响应消息中删除。如果原始请求的Request-URI 是一个SIPS URI ,那么代理服务器不应对非SIPS URI 进行递归处理。如果代理服务器对3xx 响应的所有contact 地址都进行了递归处理,那么它就就不应当把由此产生的无contact 地址的响应加入到响应上下文中去。
在将3xx 响应加入到响应上下文之前把被递归处理的contact 地址从其中删除,可以防止上游下一个实体重复尝试这个地址。
3xx 响应消息中可能同时包含SIP 、SIPS 以及非SIP URI 。代理服务器可以对SIP 和SIPS URI 进行递归处理,把其它类型的URI 放在响应上下文中, 它们以后可能会在最终响应中返回。
如果代理服务器收到了一个416(不支持的URI 方案) 响应,该响应对应的请求的Request-URI 方案为非SIP 类型,而所收到的原始请求消息的Request-URI 却是SIP 或SIPS 方案,即代理服务器在代理转发一个请求消息时将其Request-URI 从SIP 或SIPS 改成了其它类型,那么代理服务器应在目标地址集中加入一个新的URI,这个URI 应是该非SIP URI 的SIP URI 版本。如果那个非SIP URI 是tel URI, 那么URI 转换规则如下: 把tel URI 的telephone-subscriber 部分作为SIP URI 的user 部分; 把前一请求
发送的主域作为SIP URI 的host 部分。有关如何从tel URI 构造SIP URI 参见本规范第19.1.6 。
代理服务器通过SIP 或SIPS URI 来“递归处理”416 响应,同3xx 响应,这个416 响应也不应当被加到响应上下文中。
12.7.5 检查响应是否需要立即转发
以下响应都必须立即转发,直到服务器端事务发送最终响应:--除100(Trying )之外的临时响应--2xx 响应
有状态代理服务器收到的6xx 响应不必立即转发。但是它应取消所有进行中的客户端事务,参见本规范12.7.10, 并且不可以在响应上下文中再创建任何新的对话分支。
同RFC2543 要求代理服务器对6xx 响应必须立即转发的要求不同。INVITE 事务中,在其它对话分支上可能会收到2xx 响应, 此时代理服务器必须转发这个2xx 响应。如果要求立即转发6xx 响应, 会导致某个UAC 收到了6xx 响应之后又接着收到2xx 响应。本规范要求, 代理服务器在收到6xx 响应后将发出CANCEL 请求,通常这会使所有未决的客户端事务都收到487 响应,这样,代理服务器才能往上游方向转发6xx 响应。
在服务器端事务发出最终响应之后,代理服务器必须立即转发INVITE 请求的2xx 响应。
除此之外,有状态代理服务器决不可以立即转发其它任何响应,特别100(trying )响应。那些以后可能作为“最优”的响应被转发的响应应放在响应上下文中。
有状态代理服务器对非INVITE 请求恰好只转发一个最终响应;对INVITE 请求转发一个非2xx 响应或者一个及多个2xx 响应。
12.7.6 选择最优响应
经过上述步骤, 没有最终响应消息被立即转发, 并且响应上下文中的所有客户端事务都已终止, 那么有状态代理服务器必须给响应上下文的服务器端事务发送一个最终响应。
有状态代理服务器必须从已收到并存放于响应上下文中的响应中选出一个”最优”的最终响应。
如果响应上下文中没有最终响应,代理服务器必须发送一个408 (请求超时) 响应给服务器端事务。
否则,代理服务器必须转发一个存在于响应上下文中的响应。服务器首先选择响应上下文中的6xx 响应。如果上下文中没有6xx 响应, 服务器就应当选择响应上下文中保存的最低类别的响应, 并可以在该类别中选择任意一个响应。代理服务器应优先考虑那些对重发请求提供有用信息的响应。如选择的是4xx 类响应,就应该优先考虑401,407,415,420 以及484 响应。
除非代理服务器能确定所有被转发的后续请求都会产生503 响应,否则它就不可以向上游转发所收到的503( 业务不可使用) 响应。如果代理服务器转发503 响应则表示自己不能再处理之后的任何请求消息。如果所收到的唯一响应是个503 响应,代理服务器则应产生一个500 响应并向上游转发。
例如,如果代理服务器向四个地点转发了一个请求,并且分别收到了503,407,501 和404 响应,

那么它会转发407( 代理服务器需要鉴权信息)响应。
对话建立过程中可能产生1xx 响应和2xx 响应。当请求中不包含To 标签时,UAC 就用响应中的To 标签来区分创建对话的请求消息的多个响应。如果请求中不含To 标签, 代理服务器就不能在1xx 或2xx 响应的To 头字段中插入标签,同时,代理服务器也不能修改1xx 或2xx 响应中的To 标签。
由于代理服务器不能在没有To 标签的请求消息的1xx 响应中插入To 标签, 那么它自己也不能发送非100 临时响应。然而, 它可以把该请求转给一个与自己共享同一实体的UAS ,该UAS 可以返回自己的临时响应,从而与请求的发送者进入对话的初始状态。该UAS 的处理不需要与代理服务器分开,它可以是一个虚拟UAS 并与代理服务器在同一代码空间中实现。
3xx 到6xx 的响应是逐跳发送的。当发送这些响应消息时, 转发实体作为UAS, 基于从下游实体收到的那些响应发出自己的响应。如果一个请求消息中不含To 标签,那么当实体简单地转发该请求的3xx 到6xx 响应时,应当保存响应消息的To 标签。
请求中如果包含To 标签的,代理服务器在转发其任何响应是不能修改它们的To 标签。
代理服务器是否替换被转发的3xx 到6xx 响应的To 标签对上游实体来说没什么不同,但保留原始标签有助于调试错误。
当代理服务器收集了几个响应的信息时,选择其中哪个响应消息的To 标签是任意的,也可产生一个新的To 标签,这样可能使错误调试更容易。例如:合并401(请求未经授权)和407(代理服务器需要鉴权)响应的认证质询以及合并未加密的和未鉴权的3xx 响应的Contact 值。
12.7.7 汇聚Authorization 头字段值
如果所选的响应是一个401 (请求未经授权)或407(代理服务器需要鉴权)响应,那么代理服务器必须收集响应上下文中收到的所有的401 和407 响应中的任何WWW-Authenticate 和Proxy-Authenticate 头字段值,并在转发前将它们原样地加入到所选的响应中。最终发送的401 或407 响应中可能会有几个WWW-Authenticate 和Proxy-Authenticate 头字段值。
请求消息转发的任意一个或所有的目的地都可能需要鉴权证书(credential), 客户端需要接收所有的认证质询(chanllenge) 并在请求重发时为每一个质询提供鉴权证书。因此上述做法是必须的。具体内容参见本规范第26 章。
12.7.8 Record-Route
如果所选响应包含一个由当前代理服务器提供的原始Record-Route 头字段值,那么代理服务器可以在转发响应前重写这个头字段值。这样, 代理服务器能对相邻的上游及下游实体提供不同的URI 。对于多宿主主机就可以使用这种机制。
如果代理服务器是通过TLS 接收请求,然后用非TLS 连接将其转发出去, 那么代理服务器必须把响应消息中Record-Route 头字段的URI 重写为SIPS URI 。如果代理服务器是通过非TLS 连接收到的请求,然后用TLS 连接将其转发出去,那么代理服务器必须把响应消息中Record-Route 头字段的URI 重写为SIP URI 。
代理服务器加入的新的URI 与插入到请求消息中Record-Route 头字段的URI 除了必须满足同样的要求外(参见本规范16.6 节), 还要作如下修改:新的URI 不应当包含传输参数, 除非代理服务器明确相邻的上游实体支持该参数所表示的传输方式,并且该实体又将出现在后续请求的传输路径上。
当代理服务器决定修改响应消息的Record-Route 头字段时,它需要找到它所插入的Record-Route 头字段值。如果请求消息发生了螺旋路由, 每次重复经过代理服务器时, 都会被插入一个Record-Route 头字段值。本规范建议,当代理服务器要重写Record-Route 头字段值时,它在请求消息中插入的URI 应当十分明确,这样重写时才能便于查找。另外一种方法是,代理服务器在URI 的user 部分附加一个唯一性标识符,以标记此该代理服务器。
代理服务器在响应消息到达时修改标识符与实例相匹配的第一个Record-Route 头字段值。修改后的URI 中user 部分不再附加实例标识符数据。下一次重复经过该服务器时,使用相同的算法,查找最上面的与实例标识符参数相匹配的Record-Route 头字段值,就可以准确地找出上一次所插入的Record-Route 头字段值。
对于有些请求,虽然代理服务器向其加入了Record-Route 头字段值, 但其对应的响应中可能并不包含Record-Route 头字段。如果响应中确实包含了Record-Route 头字段,该字段包含代理服务器以前所加入的值。
12.7.9 转发响应
经过上述的处理,代理服务器可以对选定响应进行任何特定操作。代理服务器不可以添加、修改、或者删除消息体。除非另有说明,否则代理服务器决不能删除Via 头字段值以外的任何其它头字段值, 关于Via 头字段值的删除参见本规范16.7 节。代理服务器不可以删除顶端Via 头字段值(即原始响应消息的第二个Via 头字段值)中可能出现的任何received 参数, 该参数是在处理该响应的请求时加入的。代理服务器必须把响应消息发送给与响应上下文相关的服务器端事务进行处理。这将使响应消息被发送到最顶端Via 头字段值所指的位置。如果服务器端事务不再可用,代理服务器必须用无状态方式转发响应,直接把消息发送到传输层。服务器端事务可能会指示响应消息发送失败,或者发出状态机超时信号。这些出错信息可能会被记入日志以用于错误诊断,本协议并不要求代理服务器必须采取任何补救措施。
即使最终响应已经发出,只要相关事务没有全部终止,代理服务器也必须一直维护响应上下文的内容。
12.7.10 生成CANCEL 请求
当代理服务器转发一个最终响应,或者当它收到6xx 响应时,它都必须对响应上下文的所有未决客户端事务发出一个CANCEL 请求。本规范中,未决客户端事务是指已经收到了临时响应,但没有收到最终响应,并且没有收到相关的CANCEL 请求的事务。关于CANCEL 请求的产生参见本规范9.1 节。
转发最终响应之后取消所有未决客户端事务并不能保证终端不会收到多个INVITE 请求的200 (OK)响应。在CANCEL 请求发出并得到处理之前,可能在多个对话分支上就已经产生了200 响应。因此,本协议的扩展协议以后可能会重写这个发送CANCEL 请求的要求。
12.8 处理定时器C
如果定时器C 超时,代理服务器必须以新值来重置定时器,或者终止相应的客户端事务。如果客户端事务已收到临时响应,代理服务器必须为之生成一个匹配的CANCEL 请求;如果客户端事务没有收到临时响应,代理服务器必须按其收到了一个408(请求超时)响应来处理。
代理服务器通过重置超时的定时器C 来根据当前情况动态地延长事务的生存期。
12.9 处理传输错误
如果传输层通知代理服务器在请求消息转发时发生了错误(参见18.4 节), 代理服务器必须按收到了一个503(业务不可用)响应的情况来处理。
如果代理服务器在转发响应消息时被通知有错误发生( 参见18.4 节), 它必须放弃该响应。代理服务器不应因此取消任何与响应上下文相关的未决客户端事务。
如果代理服务器取消它的未决客户端事务,某个恶意的或出错的客户端就可以通过其对应的Via 头字段值使当前代理服务器所有的事务失败。
12.10 CANCEL 请求的处理
有状态代理服务器在收到了请求消息的临时响应后可以对它产生的任何其它请求发送一个CANCEL 请求。当收到CANCEL 请求时,代理服务器必须取消与之匹配的响应上下文的所有未决客户端事务。
由于终端会通知事务终止的事件,所以有状态代理服务器一般不需要基于INVITE 请求中Expires 头字段指示的有效期来来对INVITE 请求的未决客户端事务产生CANCEL 请求。。
当CANCEL 请求在由它自己的服务器端事务处理时,有状态代理服务器不会为它创建新的响应上下文。反之,代理服务器从现有的响应上下文中查找与CANCEL 请求相关的那个请求所在的服务器端事务。如果找到了匹配的响应上下文,那么代理服务器必须立即对CANCEL 请求返回一个200(OK) 响应。这种情况下当前实体就作为一个UAS 。另外,代理服务器必须为响应上下文中的所有未决客户端事务产生CANCEL 请求。
如果没有找到相匹配的响应上下文,当前实体没有任何与CANCEL 请求对应的请求的信息。它必须以无状态方式转发这个CANCEL 请求。
12.11 总结
如果没有相反的本地策略,代理服务器对于包含Route 头字段的请求所执行的处理可被总结为如下几步:
1. 1.检查请求的Request-URI 。如果它指示的是一个由当前代理服务器负责的资源,那么代理服务器将用从定位服务中得到的地址代替它。否则,代理服务器将不对它作改动。
2. 2.代理服务器将检查最顶端Route 头字段中的URI 。如果它指向当前代理服务器,那么将其从Route 头字段中删除。
3. 3.代理服务器将把请求转发到最顶端的Route 头字段值中URI 所指的资源位置, 如果没有Route 头字段出现则转发到Request-URI 所指的资源位置。在转发请求时代理服务器通过对目标URI 运用RFC3263 中所描述的过程来确定所用的IP 地址、端口和传输方式。

如果在请求消息的传输路径上没有遇到严格路由的实体,Request-URI 将总是指向请求的目的地。
1. 12.11.1 示例
2. 12.11.1.1 基本SIP 梯形

该示例是一个基本的SIP“ 梯形”结构,U1->P1->P2->U2,U1 和U2 表示终端,P1 和P2 表示代理服务器。两个代理服务器都要插入record-route 头字段值。这里给出消息流程:
40
YD ××××—××××
U1 发送请求给P1:
INVITE sip:callee@domain.com SIP/2.0
Contact: sip:caller@u1.example.com

P1 是一个外拨代理服务器,它不负责domain.com, 所以它进行DNS 查询并将请求发往那里。它还
在请求中加了一个Record-Route 头字段值: INVITE sip:callee@domain.com SIP/2.0 Contact: sip:caller@u1.example.com Record-Route: <sip:p1.example.com;lr>
P2 收到了这个请求。它负责domain.com ,因此它运行定位服务并重写Request-URI 。它也加入了一个Record-Route 头字段值。因为请求中没有Route 头字段,所以它对新的Request-URI 进行解析以确定向何处转发请求:
INVITE sip:callee@u2.domain.com SIP/2.0
Contact: sip:caller@u1.example.com
Record-Route: <sip:p2.domain.com;lr>
Record-Route: <sip:p1.example.com;lr>

位于u2.domain.com 的被叫方收到了请求并以200 OK 作答:
SIP/2.0 200 OK
Contact: sip:callee@u2.domain.com
Record-Route: <sip:p2.domain.com;lr>
Record-Route: <sip:p1.example.com;lr>

同时,位于U2 的被叫将其对话状态信息中的远端目标URI 设为sip:caller@u1.example.com 并将其路由集设为:
(<sip:p2.domain.com;lr>,<sip:p1.example.com;lr>)
这个200 响应沿着P2->P1->U1 的路径正常回传。现在,U1 把自己对话状态信息中的远端目标URI 设为sip:callee@u2.domain.com 并将其路由集设为: (<sip:p1.example.com;lr>,<sip:p2.domain.com;lr>)
既然路由集中的所有元素都包含lr 参数,U1 将构造出如下的BYE 请求:
BYE sip:callee@u2.domain.com SIP/2.0
Route: <sip:p1.example.com;lr>,<sip:p2.domain.com;lr>

就像任何其它的实体( 包括代理服务器) 一样,U1 运用DNS 解析最顶端Route 头字段值中的URI 以确定向何处发送请求。这使请求被发往P1。P1 注意到自己不负责Request-URI 所指的资源位置,因此它不改变其内容。它也确实发现自己是Route 头字段中的第一个值,所以它将该值删除,并将请求转发到P2:
BYE sip:callee@u2.domain.com SIP/2.0
Route: <sip:p2.domain.com;lr>

P2 也注意到它不负责Request-URI 所指的资源( 它负责的是domain.com ,而不是u2.domain.com)

所以它也不改变Request-URI 的值。P2 也确实发现自己是Route 头字段的第一个值,所以把这个值删去, 并通过对Request-URI 进行DNS 查询将如下的请求转发到u2.domain.com: BYE sip:callee@u2.domain.com SIP/2.0
12.11.1.2 穿越一个严格路由的代理服务器
该示例中,一个对话通过4 个代理服务器建立, 每个代理服务器都加入了Rocord-Route 头字段值。第三个代理服务器实现了严格路由过程,这些过程在RFC2543 及本协议先前的诸多演进版本中规定。U1->P1->P2->P3->P4->U2
到达U2 的INVITE 请求包含:
INVITE sip:callee@u2.domain.com SIP/2.0
Contact: sip:caller@u1.example.com
Record-Route: <sip:p4.domain.com;lr>
Record-Route: <sip:p3.middle.com>
Record-Route: <sip:p2.example.com;lr>
Record-Route: <sip:p1.example.com;lr>

U2 回应以200 OK 。之后,U2 基于第一个Route 头字段值发送如下的BYE 请求给P4:
BYE sip:caller@u1.example.com SIP/2.0
Route: <sip:p4.domain.com;lr>
Route: <sip:p3.middle.com>
Route: <sip:p2.example.com;lr>
Route: <sip:p1.example.com;lr>

P4 不负责Request-URI 所指的资源,因此保留其值不变。它注意到自己是Route 头字段的第一个值所指实体,所以将之删除。然后P4 根据当前的第一个Route 头字段值sip: p3.middle.com 准备发送请求,但它注意到这个URI 中没有lr 参数,因此在发送之前,它将请求消息变为如下格式:
BYE sip:p3.middle.com SIP/2.0
Route: <sip:p2.example.com;lr>
Route: <sip:p1.example.com;lr>
Route: <sip:caller@u1.example.com>

P3 是一个严格路由的代理服务器,它将如下请求转发到P2:
BYE sip:p2.example.com;lr SIP/2.0
Route: <sip:p1.example.com;lr>
Route: <sip:caller@u1.example.com>

P2 看到Request-URI 是自己以前放到Record-Route 头字段中的值,所以在进一步处理之前,将请求消息改写为:
BYE sip:caller@u1.example.com SIP/2.0
Route: <sip:p1.example.com;lr>
------分隔线----------------------------
顶一下
(2)
100%
踩一下
(0)
0%
------分隔线----------------------------
最新评论 查看所有评论
发表评论 查看所有评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 密码: 验证码:
推荐内容
  • VoIP的配置选择

    前面,我们已经围绕Vo I P 讨论了一些问题。现在,让我们讨论几种Vo I P 的配置和拓扑...

  • SIP协议介绍

    介绍 通信提供商及其合作伙伴和用户越来越渴求新一代基于 IP 的服务。现在有了 SIP协...

  • IP电话协议H.323协议 MGCP协议 SIP协议比较

    随着IP电话应用的普及,建立终端设备和网关的可扩展网络已成为业界面临的一大技术挑战...