I've been talking an awful lot lately about WSDAPI, Microsoft's implementation of the Devices Profile for Web Services, but I also work a good deal on the wire side of WSDAPI, and want to talk a little bit about how I interpret an edge case of a WS-Discovery message.
The wire and me
Before I dive into that, a clarification: when I talk about the wire, I'm talking about the side of WSDAPI that sends messages to other Web Services stacks. So this deals with the raw SOAP (XML) messages we send, the containers they're sent in (MIME, etc.) and the specifications that define what they look like. But also note that a lot of the time, I'm going to talk specifically about how WSDAPI deals with these messages and containers. The specifications allow many interpretations and what I describe isn't necessarily the only correct way to do things, but (unless I make a mistake) I will be describing the way WSDAPI does things, and I'm going to focus primarily on that because I can speak authoritatively on it.
What a mouthful. I hope I haven't lost you.
A quick primer on WS-Discovery Types
I'm going to talk a little bit about the <d:Types> field in WS-Discovery messages (WS-Discovery spec, PDF). Those of you who know what Types are can skip ahead to the next section.
Types are used to advertise the portTypes (defined in WSDL) that a service exposes, and to allow clients to search by discriminating on these values. The actual value of a type is a name inside a namespace, and this is represented in XML as a qName, which has a namespace prefix, followed by a local name. For example, the Devices Profile for Web Services defines a Device type, and if your XML declares the Devices Profile namespace to have a prefix of "dpws," then you can declare the Device type as "dpws:Device" just as you'd expect.
The thing is that services can (and typically do) expose more than one porttype. And the <d:Types> field in WS-Discovery messages can accept multiple types, all separated by spaces. When a service advertises a list like this, it's saying, "I support all of these types." Simple.
WS-Discovery also allows clients to search for services by sending a Probe message, and since these searches are often for services of a specific type, the Probe message allows clients to include one or more types. Services must only respond if they match all of the types specified in the search. It's also common for clients to cast a wide net and search for everything on a network, and so in those cases, the client simply doesn't include the <d:Types> element whatsoever.
The controversy: empty types in a Probe
So this leads to the question: what happens when a client specifies a <d:Types /> element, but leaves it empty? It turns out that the spec isn't particularly clear on this point. Section 5.1 of WS-Discovery (same PDF as before) describes the rules for matching types but omits the case where no types are specified. Those of you with math backgrounds may notice that the matching criteria for types is essentially the same as set matching criteria--except of course that the set defines the empty case, whereas WS-Discovery doesn't.
We're left with two ways to interpret the rule:
- No services match <d:Types />. The only real advantage in this interpretation is that it allows you to express three different sets of services: "no services" (specifying empty <d:Types />), "all services" (specifying no <d:Types> whatesoever), and "some services" (specifying 1 or more types inside <d:Types>). Of course, matching "no services" is completely useless, but I have to admit that deep down, I like the fact that you can express "no services." I suppose the completeness makes me happy.
- All services match <d:Types />. This is supported if you assume the description really is describing set matching criteria, and also makes logical sense if you consider the progression from some types (restrictive) to fewer types (less restrictive) to no types (not restrictive). And it also means that you're left without any fundamentally useless constructs, which also makes me smile...but not as much as having a complete set of expressions.
Now I can't really tell you that there's a "right" way to interpret the spec (after all, it's your interpretation) but my interpretation follows the second point, and indeed WSDAPI behaves as if all services match an empty <d:Types /> field. That said, client applications shouldn't be able to coerce WSDAPI into generating an empty <d:Types /> element (due to the ambiguity) and although I believe that's the case, I have to admit that I haven't actually verified it.
And if you'd like another data point, Vipul Modi over in the WCF team tells me that his WS-Discovery implementation follows the second behavior, as well. While we're on the topic, I'd like to thank Vipul for hashing out the logic with me on this one. If he hadn't, who knows what would have happened--perhaps I would have answered the sweet siren's call of the expression completeness instead of following the logical progression of empty set equivalence! Ahh, good times with specifications.
Stay tuned, I promise to get back to WSDAPI 101 in short order!