Based on some recent experiences, I wanted to write up a quick summary of how the UCMA runtime handles incoming SIP messages, which is somewhat different from how it works with the Lync client.
Each instance of CollaborationPlatform has its own listening port, which it keeps all to itself. A collaboration platform maps to exactly one trusted application, and it uses the listening port that was configured for that trusted application when it was provisioned in Lync Management Shell. All SIP requests for the application come in to that listening port on the server. Regardless of how many endpoints the collaboration platform is managing, the requests all arrive at the one listening port.
This is the cause of the exception you sometimes see on startup that says “Failed to listen on any port or address specified.” This just means that the collaboration platform is trying to reserve a listening port that is already in use by something else, usually another instance of the same UCMA application.
Routing to UserEndpoints vs. ApplicationEndpoints
I want to take a minute to talk about the difference between SIP request routing for application endpoints vs. user endpoints.
When a request goes to a normal Lync user, Lync Server “forks” the message to all registered endpoints for that user. In other words, it sends copies of the message to all of those endpoints, with bits of identifying information so it can tell which one is which. Since a user endpoint represents a single Lync user, requests can get forked to user endpoints just like they would to instances of the Lync client, Lync Phone Edition devices, and so forth.
Application endpoints are different. Every trusted application is tied to a pool of application servers. When a request goes to an address of record that belongs to an application – something like sip:firstname.lastname@example.org – Lync Server passes it along to the FQDN for that application server pool. This FQDN can be load balanced by a hardware load balancer or by DNS load balancing, but either way the request goes to only one of the servers in the pool, and therefore to one instance of the application. So, requests to application endpoints are load balanced rather than being forked. Unless you modify Lync Server’s default behaviour using MSPL, a request for an application endpoint will only ever go to one server at a time.
Choosing an endpoint
So, if all requests for all of the endpoints show up at the same port, how does UCMA know which endpoint gets which request? This may seem very simple on the surface, but let’s dig into it a bit.
The first thing UCMA looks for is a header named Ms-Application-Aor. The “Aor” piece here stands for address of record. If this header exists in the request, it uses the address of record in the header to identify which endpoint should get the request.
After Ms-Application-Aor, the next thing UCMA looks at is the URI in the To header.
In the easiest case, the To header contains a GRUU that represents a single endpoint. (This might be the case if, for instance, a remote endpoint is adding a new modality on an existing call. That request will only go to the one endpoint that is already handling the call.) These requests obviously get routed straight to the endpoint that is identified by the GRUU in the To header.
What if there’s a SIP URI that identifies a single user, but not a specific endpoint? This kind of SIP URI is called an address of record, and is sort of the “default” thing people usually mean when they use the term SIP URI. It might seem like UCMA has an easy job here too, but keep in mind that there’s nothing preventing a collaboration platform from managing multiple endpoints for a single user. Under normal circumstances, these requests are forked to all of the endpoints by Lync Server, and so they arrive at the collaboration platform with endpoint IDs already attached.
If it can’t sort things out with the To header, UCMA looks for a conversation ID, which is contained in the Ms-Conversation-ID header. If there is a conversation ID, it then checks whether one of its endpoints is already handling that conversation. If so, it passes along the request to that endpoint.
There are three possible results out of all of this processing:
- UCMA finds a single endpoint that should clearly get the request
- UCMA finds multiple endpoints that the request could be meant for
- UCMA can’t find any endpoints that match the request
In case 1, obviously UCMA sends the request to that endpoint.
In cases 2 and 3, UCMA has one more fallback option, which is the default routing endpoint. A single endpoint can be configured as the default endpoint for any requests handled by the collaboration platform that don’t seem to fit one specific endpoint. The linked blog post has more details on how to set up a default routing endpoint and why you might want to do this.
Finally, if there’s no default routing endpoint, UCMA rejects the request with a failure response indicating either that no matching endpoint was found or that the destination was ambiguous.
This is the basic overview of how UCMA figures out what to do with incoming messages. Interestingly, with the default routing endpoint you can sort of “roll your own” message dispatching behaviour if for whatever reason the normal UCMA behaviour doesn’t fit your needs. For instance, you can have your default routing endpoint catch all incoming requests that don’t match another endpoint, and then look at other SIP headers or other properties of the message and forward it to another endpoint in your application based on what it finds.