【原文地址:】
One of the most useful (and most confusing) things you can do with Microsoft SIP Processing Language (MSPL) is change the routing behaviour of Lync Server. There are a variety of reasons why you might want to do this, but in this post I want to discuss a specific case: rerouting calls to a UCMA application.
Let's say, for purposes of illustration, that you have a UCMA application that establishes a user endpoint for each of several users in your environment and answers calls on their behalf. By default, though, Lync Server will "fork" an incoming call to all locations where the receiving user is logged in. In other words, it will send a copy of the INVITE request, with a special identifying tag added, to each network location where the user has an endpoint registered. As soon as one of the endpoints answers the call, the INVITEs to the others are canceled.
This puts a bit of a damper on the idea of having the UCMA application take that user’s calls. Yes, it can answer the incoming call for the user immediately as soon as it begins to ring, but the UCMA application itself can’t prevent the call from ringing to the user if he or she is logged into the Lync client somewhere.
MSPL offers a workaround for this problem, allowing you to override the default routing and tell Lync Server to route the INVITE request only to the server (or servers) where the UCMA application is located.
I covered the basics of MSPL scripts and how to write them in a , so I won’t go into too much detail about that here. Instead, I want to discuss the key tool you have in MSPL scripts for changing message routing, the ProxyRequest method.
ProxyRequest can be called with no parameters, like so:
if (sipRequest) { ProxyRequest(); }
This simply tells Lync Server to go ahead and send the request on to wherever it would have sent it based on the default routing rules. Normally you would use this in an application that does not proxy requests by default: i.e., it has the following element at the beginning:
It's important to note that this should be in a script that filters only INVITE requests, which is specified in an element like this:
An alternative is to wrap the code in an if statement that checks whether this is a request and what method name it has:
if (sipRequest && sipRequest.Method == "INVITE") { ProxyRequest("sip:appserver.domain.local@domain.local;gruu;opaque=srvr:interceptor:saLA7P02gZi2Ho78Ix_w2AA"); }
The GRUU is an endpoint-specific URI, so this will send the call to the exact instance of the UCMA application whose GRUU you specify.
Note: The application must have a default routing endpoint (click for more details) in order to receive the call. Why? Because the To header on the INVITE message in this case has the SIP URI of the originally intended recipient, and not the SIP URI of an application endpoint.
This is actually one of the chief advantages of the approach I am describing here: the UCMA application, when accepting the call, can tell by looking at the To header where the call was originally meant to go. This means that if your application needs to forward the call to its original destination at some point, it has the information it needs to do this. Here is an example of an incoming call handler that looks at the To header and stores it for later use:
void OnCallReceived(object sender, CallReceivedEventArgs e) { _originalRecipientUri = e.RequestData.ToHeader.Uri; e.Call.BeginAccept(OnAcceptCompleted, e.Call); }
If you don’t need the To header
What if your application will not be transferring the call to the original recipient when it is done? This might be the case if, for example, your application is simply playing a failure message to callers who are not authorized to reach the number they have dialed. In this case, you can do something a bit simpler in your script:
if (sipRequest && sipRequest.Method == "INVITE") { // Check to see if the caller is authorized if (ContainsString(sipRequest.From, "sip:authorized@domain.local", true)) { // Allow the call to continue to its original destination. ProxyRequest(); } else { // Forward the call to the UCMA app. Log("Debugr", 1, "Forwarded caller to UCMA app: ", sipRequest.From); Respond("302", "Moved Temporarily", "Contact="); } }
Instead of proxying the INVITE message to another location, this script simply responds with a 302 response code, to indicate to the caller that it should direct its call to a different URI. That other URI is specified in a Contact header which the script adds to the response. The caller, assuming it responds to the 302 code properly, will place the call again to the new URI, in this case your application.