Click here to Skip to main content
15,884,836 members
Articles / Programming Languages / C#

.NET Remoting Message Redirection Channel Sinks

Rate me:
Please Sign up or sign in to vote.
3.38/5 (20 votes)
20 Jun 20033 min read 112.2K   1.7K   27  
An upper logic layer transparent way to redirect .NET remoting calls, enabling exposure of .NET remoting services behind firewall/NAT, to anywhere.
using System;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Collections;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using System.Configuration;
using System.Text;
using System.Net;
using System.Diagnostics;

namespace Reachability
{
	public class ReachabilityData
	{
		public static ReachabilityData Load(IChannelDataStore store)
		{
			string url = store["ReachabilityData_RedirectorUrl"] as string;
			if(url==null)return null;
			return new ReachabilityData(url, new Guid(store["ReachabilityData_Slot"] as string));
		}
		public void Save(IChannelDataStore store)
		{
			store["ReachabilityData_RedirectorUrl"] = this.redirectorUrl;
			store["ReachabilityData_Slot"] = this.slot.ToString();
		}
		public ReachabilityData(string redirectorUrl, Guid slot)
		{
			this.redirectorUrl=redirectorUrl;
			this.slot=slot;
		}
		string redirectorUrl;	// tcp://globalIP:port/Redirector.rem
		public Guid Slot{get {return slot;}}
		Guid slot;
		public void RedirectMessage(bool oneway, ITransportHeaders headers, 
			byte[] requestStream, out ITransportHeaders responseHeaders, out byte[] responseStream)
		{
			Redirector r = Activator.GetObject(typeof(Reachability.Redirector), redirectorUrl) as Reachability.Redirector;			
			r.RedirectRequest(slot, oneway, headers, requestStream, out responseHeaders, out responseStream);
		}
	}

	public class ClientSinkProvider: IClientChannelSinkProvider 
	{
		private IClientChannelSinkProvider _next;

		public ClientSinkProvider(IDictionary properties, ICollection providerData) 
		{
		}

		public IClientChannelSinkProvider Next 
		{
			get {return _next; }
			set {_next = value;}
		}

		public IClientChannelSink CreateSink(IChannelSender channel, string url, object remoteChannelData) 
		{
			IClientChannelSink next = _next.CreateSink(channel, url, remoteChannelData);	
			
			ReachabilityData d = null;
			if(remoteChannelData is IChannelDataStore)
				d=ReachabilityData.Load(remoteChannelData as IChannelDataStore);
			
			if(d!=null)
				return new ClientSink(next, url, d);
			else
				return next;
		}
	}
	public class ClientSink: BaseChannelSinkWithProperties, 
		IClientChannelSink 
	{
		string url;
		ReachabilityData rdata;
		IClientChannelSink next;

		public ClientSink(IClientChannelSink next, string url, ReachabilityData data) 
		{
			this.next=next;
			this.rdata=data;
			this.url=url;
		}

		public IClientChannelSink NextChannelSink 
		{
			get 
			{
				return next;
			}
		}

		public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, 
			IMessage msg,  ITransportHeaders headers, Stream stream) 
		{
			Stream ss;
			ITransportHeaders rh;
			ProcessMessage(msg, headers, stream, out rh, out ss);
			if(ss!=null)AsyncProcessResponse(sinkStack, null, rh, ss);
		}

		public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, 
			object state,  ITransportHeaders headers, Stream stream) 
		{
			sinkStack.AsyncProcessResponse(headers,stream);
		}

		public Stream GetRequestStream(IMessage msg, 
			ITransportHeaders headers) 
		{
			return null;
		}

		public void ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, 
			Stream requestStream, out ITransportHeaders responseHeaders, out Stream responseStream) 
		{
			byte[] req = new Byte[requestStream.Length];
			requestStream.Read(req, 0, req.Length);
			
			byte[] resp;
			
			bool isOneway = RemotingServices.IsOneWay(((IMethodCallMessage)msg).MethodBase);
			requestHeaders[CommonTransportKeys.RequestUri] = ((IMethodCallMessage)msg).Uri;
			rdata.RedirectMessage(isOneway,requestHeaders, req, out responseHeaders, out resp);
			if(!isOneway)
			{
				responseStream = new MemoryStream(resp);
			}else
				responseStream = null;
			
		}
	}
	public class ServerSinkProvider: IServerChannelSinkProvider
	{
		private IServerChannelSinkProvider _nextProvider;
		public static string RedirectorUrl=ConfigurationSettings.AppSettings["RedirectorURL"];
					
		public class ServerSink: IServerChannelSink 
		{
			private IServerChannelSink _nextSink;

			public ServerSink(IServerChannelSink next) 
			{
				_nextSink = next;
			}

			public IServerChannelSink NextChannelSink 
			{
				get 
				{
					return _nextSink;
				}
			}
			public void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack, Object state,
				IMessage msg, ITransportHeaders headers, Stream stream)                 
			{
				((Redirector)state).ReturnResponse(rdata.Slot,new Redirector.Response(headers, stream));
			}
			public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack,
				IMessage requestMsg,
				ITransportHeaders requestHeaders, Stream requestStream,
				out IMessage responseMsg, out ITransportHeaders responseHeaders,
				out Stream responseStream)
			{
				// This proves that the server channel on the client(which is supposed to be behind NAT) is not used
				throw new NotSupportedException();
			}
        
			public Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack, Object state,
				IMessage msg, ITransportHeaders headers)
			{            
				// We always want a stream to read from.
				return null;
			}

			public IDictionary Properties
			{
				get { return null; }
			}        
		}
		static ReachabilityData rdata=null;
		static void WaitForRequest(object o)
		{
			Redirector r = Activator.GetObject(typeof(Reachability.Redirector), RedirectorUrl) as Reachability.Redirector;
			Redirector.Message m;
			try
			{
				while(true)
				{
					r.GetNextRequest(rdata.Slot, out m);
					if(m==null)break;
					IMessage respMsg;				// must place this sink after formatter before server transport, so message goes into stream.
					ITransportHeaders respHeader;
					Stream respStream;
					ServerChannelSinkStack stack = new ServerChannelSinkStack();					
					stack.Push(new ServerSink(theSink),r);
					
					theSink.ProcessMessage(stack,null,m.requestHeaders,new MemoryStream(m.requestStream),
						out respMsg, out respHeader, out respStream);
					if(!m.oneway)
					{
						r.ReturnResponse(rdata.Slot, new Redirector.Response(respHeader, respStream));
					}
				}
			}
			catch(Exception e){
				System.Diagnostics.Trace.Write(e.ToString());
			}
		}
		public static void StopWaitRedirectedMsg()
		{
			Redirector r = Activator.GetObject(typeof(Reachability.Redirector), RedirectorUrl) as Reachability.Redirector;
			r.RevokeDestinationSlot(rdata.Slot);
		}
		public static void StartWaitRedirectedMsg()
		{
			ThreadPool.QueueUserWorkItem(new WaitCallback(WaitForRequest));
		}
		static ServerSinkProvider()
		{
			if(RedirectorUrl!=null)
			{
				rdata = new ReachabilityData(RedirectorUrl, Guid.NewGuid());				
			}
		}

		public ServerSinkProvider(IDictionary properties, ICollection providerData) 
		{			
		}
		
		public IServerChannelSinkProvider Next 
		{
			get {return _nextProvider; }
			set {_nextProvider = value;}
		}
		
		public static IServerChannelSink  theSink;
		public IServerChannelSink CreateSink(IChannelReceiver channel) 
		{
			return new ServerSink(theSink=_nextProvider.CreateSink(channel));
			//return theSink=_nextProvider.CreateSink(channel);
		}

		public void GetChannelData(IChannelDataStore channelData) 
		{
			if(rdata!=null)rdata.Save(channelData);
		}
	}

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
zhi
Researcher
United States United States
My name is Zhiheng Cao. I major in Eletronics Engineering, in University of Tokyo. I do part time jobs of computer programming and teaching high school students English and Mathematics.

CodeProject provided me with valuable information for my programming job many times in the past. I hope I can do my part by providing goodies I have.


Comments and Discussions