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);
}
}
}