 Thursday, March 17, 2005
Mr. Tay (Softwaremaker) has tackled the rather sticky problem of securely traversing mutliple SOAP intermediaries and come up with this article over on MSDN. There's a lot of good stuff to dig into here.
The scenario William came up with reminded me of a few customer projects we had going on at Avanade before I left. We were starting to see customers interested in factoring out things like authentication and authorization into separate services that operated pretty much how William described it. Customers are definitely starting to look at multi-hop message paths -- with that in mind, you can see why Indigo puts such a focus on the end-to-end picture when it comes to things like security and reliability.
It will be fun to watch these types of scenarios evolve in the future. An open question to William: what does this scenario look like on the Indigo CTP bits?
 Saturday, November 13, 2004
There’s
a good little back-and-forth between Omri
and DaveO
on the subject of Reference Properties and Reference Parameters in
WS-Addressing.
I like to think that I understand the function and motivation for Reference Properties.
Reference Properties are little blobs of state that you can include in an
endpoint. Reference Properties contribute to the overall identity of an EPR,
and are significant when it comes to determining EPR equality (if two EPR’s
have different Reference Properties, they’re two different EPR’s).
This feature allows services to dynamically construct groups of related endpoints
easily, which can be quite handy. For example, WSE2 shows that it’s
possible to use Reference Properties to attack the general problem of response
correlation by encoding a conversation ID within the ReplyTo EPR. Dispatch can
still be based on strict EPR equality, relying on the presence of the RefProp
to disambiguate between multiple conversations happening simultaneously. It’s
a good way of ensuring that the dispatch routine doesn’t accidentally
hand Thread B the response to Thread A’s request.
Reference Parameters, OTOH, are a different beast. They are not significant
in equality comparisons and thus do not contribute to EPR identity. My big
questions are: what are they used for? Who consumes them? Why would it be
advantageous to put these things inside of the Reference Parameters instead of some
other place inside the message (either in a different header, or in the body?).
If EPR’s are about determining identity, why is it good to include stuff
in them that explicitly has no bearing on EPR identity?
I have yet to have the “aha!” moment with RefParams that I had with
Reference Properties.
 Wednesday, July 21, 2004
Kirk Allen Evans has posted some sample code
for a custom Kerberos token that works with WSE 2.0’s security system. If
you’re interested in Kerberos, it’s worth checking out. It’s
also a really good example on how to extend the WSE security token while still
playing in the general realm of WS-Trust.
 Wednesday, June 16, 2004
Jef Newsom is also taking a crack at building a transport-independent SOAP router using WSE. I have a couple of observations on his implementation.
Using Pipeline.IsIntermediary = true. That sound you hear is me slapping my forehead going “why didn’t I think of that?” Setting this should allow the incoming message to be resent using SoapSender.Send(), thereby eliminating the need to clone the full body of the message using SoapEnvelope.LoadXml(). I’ll have to test that one out.
Pipeline.IsIntermediary has an interesting effect on the XML contents of a SoapEnvelope. When IsIntermediary is set to false, the XML representation of a header is removed from the envelope when the header is processed by a filter. To see this, set a breakpoint inside of a SoapReceiver and look at the OuterXml of the envelope currently being processed – if IsIntermediary is false, there will be no addressing headers in the resultant XML string.
This has a subtle implication when it comes to security. In my implementation, the outbound message leaving the router would not contain any of the security tokens present in the incoming message. This is because I have Pipeline.IsIntermediary = false and are creating the outbound message directly from the XML representation of the incoming message. In Jef’s implementation, those tokens will not be removed from the message and will therefore be transmitted across the router. I’m not sure if this is a feature or a bug. Is trust point-to-point, or end-to-end?
Sending messages directly through an OutputChannel. I’m pretty sure that this is a bug, and really not the right thing to do. Jef’s implementation circumvents SoapSender.Send() and puts the message directly on the wire using an OutputChannel obtained from SoapTransport.StaticGetOutputChannel(). This does result in message transmission, but circumvents the router’s outbound pipeline. As a result, Jef’s router will not be able to sign or encrypt messages, or have policy applied to its outbound messages. Given that security tokens are being propagated, I think this is definitely a bad thing.
 Wednesday, June 09, 2004
WSE2 ships with some excellent sample code. However, the steps needed to get the right X509 Certificates in the right stores are not immediately clear from the docs. Here’s what I had to do to get the Secure Conversation sample working.
Note: the certificates mentioned are in the <%WseInstallDir%>\Samples\Sample Test Certificiates folder
1) Install the “Server Private.pfx” key in the LocalComputer\Personal Store
2) Install the “Server Public.cer” key in the CurrentUser\Other People Store
3) Grant the ASP.NET worker process “read” access to the server certificate in the LocalComputer store.
The last step can be accomplished with the X509 Certificate tool by pointing the tool at the LocalComputer\Personal store, selecting the WSE2QuickStartServerCertificate, and selecting “Open Private Key Properties”.
On my first attempt, I had permissions set incorrectly, which caused a fault whose messages was “<RequestSecurityToken> has one or more invalid child elements”. Not the most helpful thing in the world, but WSE is helping security here by returning intentionally obscure messages back to the client. I found a more detailed error message in the Event Log, which helped me diagnose the permissions issue and get the samples working.
 Monday, June 07, 2004
I had an abortive IM conversation with Benjamin stemming from this post over on Das Blonde. I was multi-tasking at work and he was at home typing one-handed with a baby on his knee, so our conversation didn’t get very far. So this is an attempt to put down a bunch of thoughts I’m having about per-user security and Web Services. (Note: now that I’ve written this piece, I realize that it ended up having very little to do with what I originally intended it to be about. I’ll have to come back to Michelle’s post later).
The Scenario Let’s say that I have an ASP.NET front-end to a payroll system. This consists of a set of web pages that let users view and modify company payroll information. On the back end, there’s a set of payroll web services that provides data to a number of different applications – my web app happens to be one of these. Bob is an employee at my company; he should be able to use the web app to view his information, but should not be able to see anyone else’s. Alice is the HR person for my company; she can look at anybody’s information using my web app. Finally, as an added level of security, only trusted applications should be able to send messages to the Payroll web services.
I think these requirements are representative of common business requirements. So, what’s the best way to implement the simultaneous requirements of user-level and system-level? How does role-based security play into the solution?
Problem 1: Where are the authoritative credentials stored? Authorization is the process of taking some identity claims from the user (e.g. a username/password combination) and verifying those claims against some authoritative credentials. In my scenario, there are three possible places to store those credentials.
Option A: Store the credentials on the client web app In this option, each client application would maintain its own credentials database. Thus, when Alice logs in to my web app, the first thing I do is check her identity claim against her username/password information that I’ve stored locally. Once I’ve verified Alice’s identity, I can send messages to the Payroll web service to the effect of “I am the PayrollWebClient system acting on behalf of Alice (trust me, she’s really Alice!), and she’d like to see her last paystub”. The payroll service trusts the web client to make sure that’s really Alice sitting at the keyboard, and fulfills the request.
Implementation-wise, this could be accomplished by issuing X509 certificates to every trusted system that’s allowed to call the Payroll service. All messages sent to the Payroll web service would need to be signed and encrypted with one of these trusted certificates, and we’d assert this requirement via policy. This would be sufficient to establish system-level trust. The user context for a given SOAP message could just be conveyed in a SOAP header; since the service trusts the app sending the message, the correctness of this user-level information can just be taken on faith (sidenote: what would this user identity information look like, exactly? A username token with no password? Something else?)
The downside to this is that identities are now scoped to the client application, so if Alice and Bob need to use more than one application they need to remember more than one username and password. Usability degrades and application management becomes difficult.
Option B: Store the credentials with the service
In this option, the client application is responsible for collecting identity claims but does not validate them. Instead, it passes those claims on to the service in every subsequent message sent on that users behalf. System-level trust is handled in the same way as Option A. In effect, messages sent using this options say “I am the Payroll WebClient acting on behalf of a user who claims to be Alice; if she’s really Alice, she’d like to see her last paystub.”
This option seems the easiest to implement in WSE2. My client application is now responsible for keeping track of which claims belong to which user, and appending those claims to every envelope I send on behalf of that user. I might choose to accomplish this by creating a UsernameToken based upon what they entered in the login box, and then appending that to each envelope via SoapContext.Current.Security.Tokens. I’m pretty sure you could set up a policy to assert the presence of both an X509 cert for system-level authentication and a UsernameToken for user-level authentication. Alternatively, I could take advantage of Secure Conversation and implement a custom SecurityTokenManager on my web app, so I didn’t need to reauthenticate at the server side on each request (but this would basically just be an optimization, not a fundamental change to the security architecture).
The downside: It’s possible that Alice still has multiple identities. What happens if my client app needs to invoke two different services in two different security realms? I now have to maintain multiple set of identity claims for Alice. Worse yet, Alice has to provide me all of those identity claims, meaning she has to enter multiple username/passwords if her credentials are not identical in both security realms. Again, management and usability become problematic.
Option C: Store the credentials with a trusted 3rd party authentication service
In this option, all services agree to federate identity with a trusted third party. When Alice logs in, my web app passes her identity claim to the authentication service, which validates her identity and issues back some token. By including this token on all subsequent messages my system has can say “I am the PayrollWebClient, acting on behalf Alice (as verified by someone we both trust). She’d like to see her paystub.” This seems like it would solve all our management problems; Alice has only one authoritative identity and everyone agrees to use that. Definitely more manageable than A or B.
The downside: realistically, this is only feasible in the context of Secure Conversation. Otherwise, you double the number of service calls (every message must be first sent to the authenticator, and then to the service). Secure Conversation means that you only need to authenticate once to establish security context. Unfortunately, support for 3rd-party token issues was removed at the last minute from WSE2. One possible option would be to roll your own SCT, but in doing so you risk compromising interoperability down the road.
Problem 2: Now that we know who Alice is, what can she do?
Identity verification is only one piece of the puzzle. It’s all well and good to know that Alice is in fact who she says she is, but that information is almost useless unless you have some idea of what her “Aliceness” implies. In my scenario, Alice is an administrator with special privileges. This is a classic role-based security problem which becomes more complicated in the web services world. Role-based security boils down to two sub-problems: membership and enforcement. Membership information is usually stored with identity information, while enforcement should be done as close to the code subject to the security restrictions as possible. Given these requirements, it makes sense to look again at the three options for storing identity:
Option A: Credentials-on-client
This option really starts to look ugly when you take role-based security into account. Since identity (and now role) information is scoped to the client application, you get this proliferation of roles and identities. Does one client’s concept of “admin” mean the same thing as another client’s concept of “admin?” Enforcing role-based security at the service is now very difficult, because the service needs to know about all the applications that might invoke it (so it can know about all the possible roles they might define). The only really manageable option would be for each client to enforce security restrictions, cutting off access to a restricted service before the message is ever sent. Problem is, now you’ve embed what is effectively a service-level rule into each client that calls it. What happens when those security requirements change? Doesn’t feel right to me.
Option B: Credentials-on-service This option is definitely more appealing than Option A, because the service owns the roles as well as the identity information. With this option, the service can guarantee that only one authoritative “admin” role exists (at least, as far as it’s concerned – if a client needs to call two different services as “admin”, there’s still the multiple-identity problem as above). Thus, the service can enforce its own security rules and role enforcement is done at the service instead of the client, which is IMHO how it should be done. The downside here is that the client has no longer has any information about what a user can or cannot do. This leads to a usability problem – say you have some secured form that takes 4 screens to fill out. If you knew up front that the client didn’t have the security clearance to submit the form, you could spare your user the pain of filling out a form that has no hope of being filled out correctly. However, you can’t take that “up front” action because you don’t know who that user actually is until they send a message to the service.
Option C: Trusted 3rd party authenticator Again, this seems like the best of the three. Centralize all identity and role information inside of a trusted third party, and let that 3rd party be the authoritative source for identity verification and role membership. No more multiple roles, meaning that security enforcement can be localized to the service, where it belongs. Furthermore, if SCT’s are used, the client could look inside the issued SCT for role information, allowing role-based security enforcement at both the client and the service if needed. It seems like a federated identity system would bring all of the conceptual goodness of Active Directory, only “servicified”.
And now for the downside…as I said before, there’s no out-of-the box mechanism for issuing 3rd party SCTs using WSE2. Furthermore, I don’t think there’s any standardized way of exchanging role information in the context of an SCT – SAML and WS-Federation are still a ways out. Rolling your own is an option, but with the associated interoperability risk. However, we’re still way better off now than we were with WSE1. At least with WSE2, we’re only fighting about wire format – we’re still figuring out what the best way to convey role information inside of an SCT is, but at least we have the infrastructure in place to issue, renew, exchange and validate those SCT’s already done for us.
Conclusion WSE2 is a big step forward in term of secure web services, but it’s not the endgame just yet. There’s a lot that can be done with WSE2 out of the box directly. Integrating with Windows domain authentication is a big step; it solves one part of the role-based security puzzle. If all your services can talk to the same NT credential store, you can auto-issue SCT’s to your heart’s content and at least have what I describe as “Option B” taken care of (with the added benefit of having a unified cross-service credential store). It’s not the general-case, interoperable solution that pan-enterprise web services need to succeed, but it’s a step in the right direction. The real solution lies in federating identities, and that’s something for which we’ll just have to wait.
 Saturday, June 05, 2004
My first post on SOAP routing with WSE2 detailed some of the issues I ran into while trying to build a transport-agnostic replacement for the SoapHttpRouter. Although I referred to these issues as “routing roadblocks” (I’m a sucker for alliteration), I found workarounds for all of them. The result was this small chunk of code. There are problems with this code as written though: it only operates on the forward path (the Destination address) and ignores the return path (the ReplyTo/FaultTo/From headers).
Consider a duplex communication scenario. There are two services, each with a transport-agnostic Address URI. The first service, Svc1, listens for requests and sends responses. The second service, Svc2, listens for responses. When a message arrives at Svc1, a response is generated and sent back to Svc2 according to the ReplyTo endpoint present on the original message. Suppose that there are two servers involved here – Server A sends messages to Svc1 hosted on D, which processes the request and sends the message back to Svc2 on A. This general pattern of communication is illustrated by the TcpStockServiceSoapReceiver sample that ships with WSE2.
Now, let’s assume that A and D are separated by an arbitrary number of trusted intermediaries. The exact structure of this intermediary chain is unknown to both A and D; each server only knows about the next link (in deference to the “next hop” routing style preferred by WS-Addressing). Furthermore, let’s also assume that neither server can talk to each other directly. This last requirement implies that both the request and response messages need to travel through the intermediary chain. Thus, the intermediary must alter not only the Destination endpoint, but the ReplyTo/FaultTo/From addresses as well.
For the sake of example, let’s say that there are two intermediaries B and C separating our two servers. Outbound messages should flow from A -> B -> C -> D, and from D -> C -> B -> A on the return path. To figure out what sort of routing information B and C would need to store, I drew up the scenario on my whiteboard. The resultant picture looked like this:

Make this bigger -- RoutingScenario.jpg
A couple of observations: first, I constructed this scenario so that the Address URI of the destination service never changes – all routing takes place solely through the Via property of the address endpoint. Secondly, this shows that this scenario is a pure “next hop” scenario – servers have no knowledge of machines that are not directly adjacent to them.
Now let’s look at the routing tables on B for a moment. The rules for the outbound path to service one are not surprising. B listens on “Svc1 via B” and forwards the message to “Svc1 via C”. In order to preserve the return path of the response message, B must also alter the ReplyTo address and change it from “Svc2 via A” to “Svc2 via B”. What are the rules for the inbound path? According to our scenario, B must listen for responses on “Svc2 via B” and forward them on to “Svc2 via A”. Thus, B has two routing rules for the Svc2 URI.
Here is where we start to run into problems with the implementation of the ReferralCache class. The ReferralCache is essentially just a configurable URI -> URI map. However, as I’ve shown through B’s routing table, it’s not enough to ask the ReferralCache “this message is addressed to Svc2, where should it go?” because the answer depends on context. Unfortuantely, we have no way of feeding the necessary context to ReferralCache.ResolveUri(), since that function operates only on URI’s and not full EndpointReferences. Since the two entries for the Svc2 URI have different Via’s, an EndpointReference would be sufficient to differentiate the two.
The reason I included the additional intermediary C here is because it invalidates my first solution to the problem. I originally thought about having A set its ReplyTo to “Svc2/B”. That would free B from having to tweak the ReplyTo on the outbound message and therefore it would only need one routing entry to handle responses. However, it became clear that solution broke if there were multiple intermediaries in the picture. Since I’m of the opinion that the intermediary chain should be transparent so long as all those intermediaries are sufficiently trusted, a solution that worked only for exactly one intermediary was no solution at all.
The moral of the story? I don’t think I can use the ReferralCache and still get the functionality I want for my router. Not that writing an EPR->URI dictionary is so hard or anything; it just would have been nice to leverage the configurability and standardized implementation of the ReferralCache.
 Thursday, June 03, 2004
I’ve been playing around a bit with the routing samples included with WSE2. Since WS-Routing has now been subsumed by WS-Addressing, I wanted to see what the changes were to the routing story in WSE2. If you’re interested in doing the same thing, the best place to start is to look at the Routing sample that ships with WSE2. This sample is pretty good if you’re only interested in routing to HTTP endpoints. Routing to non-HTTP transports does not seem to be supported by the SoapHttpRouter (apparently, it’s called the SoapHttpRouter for a reason).
Given that I’ve been working with custom (non-HTTP) transports, I thought it might be interesting to try and create a transport-agnostic SOAP router. Conceptually, routing SOAP messages is a pretty straightforward endeavor (side note: “pretty straightforward” is software-speak for “harder than you’d think”…). Logically, a router is just a special kind of service. The router looks at the destination address of an incoming message, indexes into some table of forwarding addresses, and then forwards on the message to the new address. Easy cheesy, right? Well, pretty much – if you look at the code at the bottom of this post, you’ll see that the code isn’t really all that complex. The ReferralCache class (for now) does most of the work involved in maintaining the table of forwarding addresses, so all I really had to do was write the core messaging code. The implementation I have right now is far from done (and probably far from correct, at this point), but it does forward messages from one service to another.
Routing Roadblock #1: What endpoint is my router listening on, exactly?
EndpointReferences can encode more information than just a URI. For routing, we care about two properties: Address and Via. Given Hervey’s comments about TransportAddress, it makes sense to use the Address property of an EPR to uniquely identify the service and the Via (TransportAddress) to encode delivery information. I wanted to design my router implementation so routing would take place solely based on the Via and ignore the Address URI.
Since WSE2’s message dispatch algorithms involving matching both Address and Via, I needed to construct an endpoint that represented “any Address with this Via” and set up my router to listen on that endpoint. It turns out that there is a standard address URI called the AnyRole which can be used as a wildcard for situations like this. This special URI is represented as a constant on the WSAddressing class in Microsoft.Web.Services2.Addressing. Using this wildcard, I was able to add my router to SoapReceivers using this code:
EndpointReference routerEpr = new EndpointReference( WSAddressing.AnyRole ); routerEpr.Via = new Via( new Uri( “soap.tcp://localhost:2323” ) );
This makes my router agnostic to Address, which is what I wanted.
Routing Roadblock #2: “This envelope’s already been processed!”
If you look at the code, you’ll see that I’m duplicating the contents of the SoapEnvelope received by the router and forwarding on a new message. My first attempt did not do this; it did in-place alterations on the message passed to Receive() and resent the modified message using a SoapSender. However, I got an exception when I tried to resend the message. Basically, once a message has been processed its useful life is over; you can’t resend it again. And the “IsProcessed” bit is not user-settable, either, so my only option was to duplicate the contents of the old message using the LoadXml() function on SoapEnvelope.
Routing Roadblock #3: What to do about MessageId? Every message carries around a unique MessageId as part of its addressing headers. This MessageId is very important, because it’s how the message originator will correlate subsequent responses back to the message that evoked them (responses will carry the original MessageId in the RelatesTo header). If the originator is using a duplex (asynchronous) messaging pattern, the RelatesTo header will be the only mechanism of contextualizing the response. Thus, it’s important for any intermediaries to preserve MessageId as a message flows through the chain – otherwise, everyone will get confused.
Since MessageId is read-only, I ended up having to reuse the AddressingHeader structure from the original message as the addressing header block for the forwarded message. If I had done a copy-by-value here, messages would spontaneously take on a new MessageId as they passed through the router.
Solving these 3 problems got me to a very simple (read: dumb) implementation that can forward requests to different addresses and transport types. The implementation below covers the outbound case, but does not do anything to fix up addressing headers relating to response messages (ReplyTo/FaultTo). There’s a reason for this, which I’ll have to cover in a different post because this one has rambled on for long enough already.
Code: SoapRouter.cs.txt
 Wednesday, June 02, 2004
In order to use a custom transport with WSE2, you need to map the transport’s URI scheme to a transport provider. You can accomplish this programmatically like so:
using Microsoft.Web.Services2.Configuration. WebServicesConfiguration.MessagingConfiguration.AddTransport( “soap.smtp”, new SoapSmtpTransport() )
Contrary to popular belief, it’s possible to do this via configuration as well – although the documentation for WSE isn’t really clear about this. After a little bit of sleuthing in Reflector, I came up with the following configuration which seems to work quite well: <microsoft.web.services2>
<messaging>
<transports>
<add scheme = "soap.smtp" type="Net.Hyperthink.Samples.SoapSmtp.SoapSmtpTransport, Net.Hyperthink.Samples.SoapSmtp" />
</transports>
</messaging>
</microsoft.web.services2>
 Tuesday, June 01, 2004
SOAP
routing is a tough problem. On one hand, you want the ability for a message to transparently
pass through a chain of intermediaries. The flexibility and architectural
creativity enabled by dynamic message routing is attractive. On the other hand,
dynamic message routing is basically a man-in-the-middle attack
waiting to happen. When it comes to routing, balancing flexibility and security
is tough.
Signing the addressing headers helps guarantee security. Binding the addressing
headers to the message body with a signature ensures that your message will end
up at a service whose address URI matches the Address URI to which the message
was sent. Routing capability is accomplished through the Via property of the
Destination EPR (which is not present on the wire, and thus not included in any
signature). Machine A can send a message to “urn:foo via soap.msmq://private”,
which can be picked up by Machine B and sent to “urn:foo via http://public”. The “urn:foo” part
of the destination cannot be altered without destroying the integrity of the
signature. Thus, Machine A is guaranteed that the message will be delivered to
some service named “urn:foo”.
Simple
signatures do not, however, guarantee that your message will get to the real
“urn:foo” service. A hacker could set up a malicious “urn:foo”
service and deliver your message to “urn:foo via soap.tcp://malicious”
by compromising one of the intermediaries in the chain. Securing your message
against compromised intermediaries can be accomplished with encryption; you can
encrypt the message body with the asymmetric key of the real “urn:foo”
service. That way, even if a malicious router sends the message to someone you
don’t trust, that message will be useless to them because they lack the
appropriate key to decrypt it. Alternatively, you could encrypt the addressing
headers so that only trusted routers can decrypt them. Realistically, in the
name of paranoia, I think you’d probably be smart to do both if you’re
really concerned with message security.
 Wednesday, May 26, 2004
Hervey has posted a sample implementation of a soap.udp:// transport for WSE2. That protocol offers some interesting options when it comes to IPV4 multicasting. Since there might be more transports becoming available, I added a wiki page over at SoaWiki to keep track of Wse2CustomTransports.
I’m really excited about the possibilities offered by WSE2’s support for custom network transports. As computer networks have evolved, the industry has developed a number of different network protocols to solve various different message distribution problems. WSE2 now offers the glue necessary to allow us to take advantage of these different protocols, while uniting them with a common message format and a unified set of message processing rules.
SOAP messaging isn’t just an HTTP thing anymore.
 Tuesday, May 25, 2004
I finished up my sample implementation of an SMTP transport for Wse2. Thanks to the power of WSE, you can now easily call Web Services via email :)
The code is attached to this message. Instead of writing up a full article on the implementation, I peppered the code extremely liberally with comments. As a result, the explanation of the code is interleaved with the code itself. I’ll probably write more in depth about specific parts of the implementation, but hopefully the comments in the code should suffice for now.
Thanks to Pawel Lesnikowski for the excellent POP3 library. You can find that implementation on the web here.
A couple of notes:
- The design is heavily inspired by the SoapTcpTransport. I tried to stick to their pattern as much as possible. That said, any bugs are mine -- not theirs.
- The transport requires some basic configuration. I’ve included a sample App.config in the project which can be used in client applications.
- By default, the SMTP server used for sending outgoing messages is localhost. As long as you’re running IIS, this should work fine. If you want to use an external SMTP server, you can specify it by setting the “SmtpServer” key in AppSettings.
- Soap.smtp endpoints are addressed like so:
EndpointReference toEpr = new EndpointReference( new Uri( "urn:stockservice" ) ); toEpr.Via = new Via( new Uri( @"soap.smtp://soap@hyperthink.net" ) );
I have a sample implementation of the TcpAsyncStockService running using soap.smtp://, but I’m not exactly sure if I can post that code. However, thanks to the awesome transport architecture of WSE, changing from soap.tcp:// to soap.smtp:// is a really simple process. All you have to do is change an endpoint reference and you’re off to the races.
Happy hacking…
Download: Net.Hyperthink.Samples.SoapSmtp.zip
 Monday, May 24, 2004
I
work in a fairly large office building with a central mailroom. The existence
of this mailroom greatly simplifies life for both me and the postal service
employee who delivers mail to my building. I have a centralized place to pick
up my mail, and the mail carrier doesn’t have to hunt around the entire
building looking for every individual office. Instead, there’s one guy at
my company whose job is to take the bundle of incoming mail and distribute it to
the mailboxes of each person in the building. In this way, large numbers of
messages can be delivered to their appropriate recipients quickly, with minimal
work on everyone’s part.
There’s an analogy between an office mailroom and the messaging internals
of WSE2. Both systems, in effect, solve similar problems, and by looking at how
one functions it is possible to understand the other.
My office building houses many message destinations (people) within a single
physical address. The WSE2 analogy of my office building is an application that
hosts a set of services. Although this application might host a large number of
services, it only needs one transport address. For example, many different
services can be hosted over the same TCP port. One address – many destinations.
This is a new notion in WSE2, made possible by WS-Addressing. In ASMX web
services, the concept of “service identity” and “transport
address” are bound up into a single URI. In other words, the problem of
logically identifying a service is inextricably linked with the problem of
delivering messages to it. In WSE2, services have an “identity URI”
which uniquely identifies the service for things like policy application as
well as a “transport address”, which tells the infrastructure how
to get messages from point A to point B. Keeping these two ideas separate is
important when you start thinking about exposing a service over a variety of
network transports. WSE addresses this separation through the
EndpointReference.Via property – the details of which can be found over on Hervey’s
blog. For the purposes of this analogy, though, it’s sufficient to
say that many services can be hosted on the same transport address in the same
way that many people can have mail delivered to the same office building.
By the time a letter arrives at my office building, it’s already traveled
through a complicated infrastructure in order to get from its original sender
to my building. From the perspective of my office mail clerk, though, this
process “just happens” and he doesn’t have to be concerned
about its internals. All the mail clerk has to do is be familiar enough with
the protocols of the network to transfer messages on and off of it – in the
real world, this means signing USPS delivery confirmations and filling out
shipping labels. In the WSE2 analogy, the infrastructure for exchanging
messages with the outside world is implemented using some sort of standardized
message exchange technology (e.g. TCP, UDP, SMTP, HTTP, MSMQ, etc). The WSE 2.0
“mail clerk” must implement enough of the network protocols to move
messages on and off of the underlying network.
In WSE2, the “mail clerk” is a concrete implementation of the
SoapTransport class. Each particular network technology has its own
SoapTransport implementation – WSE2 ships with implementations for TCP
and HTTP, and it’s possible to extend the architecture to other
transports by implementing custom derivations of SoapTransport. A SoapTransport
implementation is responsible for maintaining a pool of network resources over
which messages might arrive. The TCP transport, for example, maintains a set of
TCP sockets in “listen” mode. Similarly, an MSMQ transport might
maintain a pool of MessageQueue objects. As such, the transport serves as an
abstraction barrier between the network and the rest of the messaging
architecture. It encapsulates the details of the underlying network so that
they are hidden from higher layers of the messaging stack.
In my office, every person has a mailbox that stores incoming mail until the
recipient gets around to picking it up. When the mailman arrives at my office
building, he leaves a large bundle of incoming mail with the mail clerk. The
mail clerk is responsible for looking at the address of each letter and
delivering it to the proper mailbox. In WSE2, the analogy to “mailbox”
is an ISoapInputChannel implementation. An InputChannel is like a mailbox for
services – it stores messages that are addressed to a specific service
until that service can come and pick them up.
The transport class is responsible for the creation and maintenance of input
channels. Every SoapTransport must implement ISoapTransport.GetInputChannel(
EndpointReference epr, SoapChannelCapabilities c ). This method opens an input
channel on a specific endpoint. As part of the GetInputChannel() operation, the
transport class must open a new network resource (exactly which network
resource is derived from the EndpointReference.TransportAddress property,
according to the semantics of the transport). For example, as part of opening
an InputChannel on the “soap.tcp://localhost:2323” URI, the TCP
transport might open a TCP socket on port 2323 – but in general it’s
up to each individual transport implementation to determine how to map
transport addresses into network resources. The transport must also store the
newly created input channel in an internal collection (conveniently implemented
by the SoapTransport.InputChannels collection), so it can deliver messages to
that channel when they arrive later.
Just like the mail clerk, the SoapTransport implementation must look at the
address of each incoming message and deliver it to the appropriate
InputChannel. This operation can be done generically for all transport (thanks
to the transport-independent addressing mechanism that WS-Addressing provides).
As such, dispatch logic is implemented in a single place – SoapTransport.DispatchMessage().
Thus, when a message arrives on a the network, the transport is responsible for
deserializing that message from the network and calling
SoapTransport.DispatchMessage(). DispatchMessage() then looks at the addressing
headers of the message and matches them against the all of the active
InputChannels the transport maintains in its InputChannels collection. If a
match is found, dispatch message calls InputChannel.Enqueue to store the
message in the InputChannel.
When a message arrives in my office mailbox, it stays there until I pick it up.
Similarly, messages stay buffered in an InputChannel until the service that
opened the channel can come along and process it. Realistically, I don’t
think that messages really accumulate inside of the InputChannel because the
service infrastructure picks them up almost as soon as they come in – but
conceptually, the InputChannel offers the same buffering capabilities as a real
mailbox.
The problem with my office mailroom is that I periodically have to poll my
mailbox for new messages. Being the lazy programmer that I am, what I’d
really like is for an intern to watch my inbox for me and run over to my office
with the new message every time one arrives. WSE2 allows services a similar
degree of laziness; the service class (whatever concrete subtype of
SoapReceiver that happens to be) is not responsible for monitoring the status
of its input channels directly. Instead, there’s an intermediary –
an intern, if you will, that takes care of delivering the message from the
input channel to the SoapReceiver automatically. This “intern” is
the SoapReceivers collection.
In order to register a listening service, you must register it with the
SoapReceivers collection by calling SoapReceivers.Add( EndpointReference epr,
SoapReceiver receiver). This binds a service instance to an endpoint and starts
the messaging infrastructure listening for messages on that endpoint. Internally,
this is accomplished by finding the appropriate instance of SoapTransport based
upon the URI scheme of the endpoint’s TransportAddress, and then calling
GetInputChannel() on that transport. The SoapReceivers collection can then call
BeginReceive() on that InputChannel, registering a generic dispatch function as
a callback. Thus, whenever a message arrives on that InputChannel, the callback
will be invoked and the SoapReceivers collection can deliver that message from
the InputChannel to the waiting service. It’s exactly the same effect as
having someone run up to your office every time a message arrives in your
mailbox, only it’s accomplished with async callbacks and no interns.
In summary, delivering a message to your office is a series of the following
asynchronous operations:
1)
Postal
carrier delivers letter to mailroom.
2)
Mail
clerk delivers it to your mailbox.
3)
Intern
runs it to your office.
The
corresponding sequence of asynchronous events in WSE2 is as follows:
1)
Message
arrives on network resource
2)
Transport
dispatches it to an InputChannel
3)
The
SoapReceivers collection dispatches it to the service class
I’ll
talk more about what I see as the benefits of this architecture later. I’ll
also be talking more about the mechanics of accomplishing all of this via a
custom transport implementation in future posts.
WSE2 is finally out, so I’m finally out from underneath an NDA. Phew, I feel like I can breathe again…
Most of my work with WSE2 thus far has been dealing with the core messaging model and transport system. I’ve learned a lot from working with it, and I’ll be blogging about some of the interesting things I’ve noticed. However, before I dive into the gory details, it’s best to describe an overview of the WSE2 messaging architecture.
WSE 2.0 offers three different layers of abstraction for dealing with messaging. Which layer you choose to program against depends largely on who you are and what you’re trying to accomplish. People who have been playing with the Tech Preview bits will recognize the following:
The Service Model: This layer is at the top of the abstraction stack, and is closest to ASMX in terms of programming model. At this layer, you’re dealing with classes and methods. On the server side, you have the SoapService base class and methods decorated with [SoapMethod]. At the client side, you have the SoapClient base class. If you use the service model, you can build secure services with full support for policy, all without ever having to touch a SoapEnvelope unless you want to.
The SoapPort Model: This layer is more message-oriented than method-oriented. At this layer, you’re not using [SoapMethod]. Instead, you’re inheriting directly from SoapReceiver and implementing the Receive( SoapEnvelope e ) method. Similarly, on the client side you’ve got SoapSender and its single Send( SoapEnvelope e ) method. At this layer, the XML message is right there in your face, ready for you to do whatever you want with it. The SoapService class from the Service Model itself inherits from SoapReceiver, so the Service Model is really just a specialization of the more abstract SoapPort model. The pluggable Input/Output filter pipeline (which, IMHO, was the coolest feature of WSE1) also operates at this level of abstraction.
The Transport/Channel Model: This is the lowest-level programming model. It’s unlikely that you’ll every really have to touch this unless you’re implementing a new network transport for WSE2. Code at this level is concerned about taking messages off the wire and dispatching them to the appropriate SoapReceiver for processing. This layer operates almost entirely behind the scenes, but does a lot of really important work.
Architecturally, the biggest change since the Tech Preview bits has occurred inside the Transport/Channel model. A lot of this change seems to have been driven by the adoption of the WS-Addressing spec and the subsequent pervasiveness of EndpointReferences. The Transport/Channel model that shipped with WSE2 is nothing short of extremely cool, and I’ll be posting more about the things that excite me about the new architecture over the course of the next few days. I’m also putting the finishing touches on some sample code that implements a soap.smtp:// transport, which I’ll post when it’s ready.
© Copyright 2008 Steve Maine
Theme design by Bryan Bell
newtelligence dasBlog 1.9.7174.0  | |  | Page rendered at Friday, May 16, 2008 7:54:40 AM (Pacific Standard Time, UTC-08:00)
Reset | BlogXP | business | calmBlue | Candid Blue | dasBlog | dasblogger | DirectionalRedux | Discreet Blog Blue | Elegante | essence | Hyperthink | Just Html | MadsSimple | Mobile | Mono | Movable Radio Blue | Movable Radio Heat | nautica022 | orangeCream | Portal | Project84 | Project84Grass | Slate | Sound Waves | Tricoleur | useit.com | Voidclass2
|
Search this blog
Archives
everything
| | Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|
| 27 | 28 | 29 | 30 | 1 | 2 | 3 | | 4 | 5 | 6 | 7 | 8 | 9 | 10 | | 11 | 12 | 13 | 14 | 15 | 16 | 17 | | 18 | 19 | 20 | 21 | 22 | 23 | 24 | | 25 | 26 | 27 | 28 | 29 | 30 | 31 | | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Navigation
Categories
|