One common question I see on Remoting forum is how to configure a proxy to use a particular channel from the set of registered channels for communicating when creating a proxy. For instance consider an Remoting application that exposes both a secure and non secure TcpChannel and expects clients for local box to use the unsecured channel and clients from other machine to use the secured one. Usually each channel has a priority based on which they are sorted in the app domain. When you register multiple channels with the same priority then they are added in the same order the user configured them. When a sink is to be prepared for a proxy request, Remoting will walk the registered list in descending order and find the first one that can serve the Uri scheme. So if you have multiple channels registered for the same transport medium you really don't have an option of choosing between them.
I thought about this and decided to write a small wrapper channel that will help me in achieving this. The solution is to register all your usual list of channels with this channel wrapper instead of registering them with ChannelServices. Then you register this wrapper channel with ChannelServices and so each time a Sink is to be prepared Remoting will call in to our channel wrapper and it will pick the channel based on user preference.
Lets take a look at this class which I call RemotingCompositeChannel.
//Implements IChannelSender to provide IMessageSink when requested
//Implements ITrackingHandler so we can wire up when an CAO proxy is unmarshalled
CompositeChannel can be created in two Uri comparison modes, Exact and WildcardMatch and their function is to match a Uri exactly or do pattern matches. You first create an instance of the composite channel and register all of your regular Remoting client channels to this composite channel. Finally register the CompositeChannel with ChannelServices.
Lets see a sample MarshalByRefObject definition
Sample client usage:
Output from the server:
Server started. Press any key to terminateAnonymousSayHello : HelloIs User authenticated: FalseAnonymousSayHello : HelloIs User authenticated: TrueAuthenticated user name: [MyDomainRemovedForSecuritySake]\mahjayar ==>Success. Used the authenticated channelAuthentication type: NTLM
Output From Client:
Registered a compositechannel that contains multiple channelsPoint MyRemoteService activation to port 8889 which is unsecured.Point MyAuthenticatedRemoteService activation to port 8891 which is secured.Unmarshalled object Server.MyRemoteServiceIS transparent ProxyType of proxy:Server.MyRemoteServiceCalling anonymous methodHelloCalling authenticated methodNotAuthenticatedUnmarshalled object Server.MyAuthenticatedRemoteServiceIS transparent ProxyType of proxy:Server.MyAuthenticatedRemoteServiceCalling anonymous methodHelloCalling authenticated methodAuthenticatedSayHello: Hello
As you can see the first client's call to AuthenticatedSayHello comes back as unauthorized and the second client (of type MyAuthenticatedRemoteService) uses the authorized channel.
PS: The usual disclaimer applies the RemotingCompositeChannel.cs class attached to the post. Its my own code and not tested by Microsoft or clarifiers for support from Microsoft. Use them in your code as you wish and if you find any bugs or have any suggestions for improvement then please post back. The code has enough comments to explain the reasoning for each method. This should work for both CAO and SAO objects. I will leave the SAO object verification up to the user.
Update: Added a link to the file from my Skydrive account.