using System;
using System.Diagnostics;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Collections;
using System.Runtime.Remoting.Messaging;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
namespace System.Runtime.Remoting.Caching
{
public class CachingClientSinkProvider: IClientChannelSinkProvider
{
private IClientChannelSinkProvider _next;
public CachingClientSinkProvider(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);
return new CachingClientSink(next);
}
}
[AttributeUsage(AttributeTargets.Method,
Inherited = true,
AllowMultiple = false)]
public class InfinitelyCachedAttribute : CacheTimeAttribute
{
public InfinitelyCachedAttribute() : base(TimeSpan.FromDays(100)){}
}
[AttributeUsage(AttributeTargets.Method,
Inherited = true,
AllowMultiple = false)]
public class CacheTimeAttribute : Attribute
{
private TimeSpan time;
public TimeSpan Time{ get { return time; }
set { this.time = value; }
}
public CacheTimeAttribute(TimeSpan time) { this.time=time; }
public CacheTimeAttribute(string time) { this.time=TimeSpan.Parse(time); }
}
public class CachingServerSink: BaseChannelSinkWithProperties,
IServerChannelSink
{
private IServerChannelSink _nextSink;
public CachingServerSink(IServerChannelSink next)
{
_nextSink = next;
}
public IServerChannelSink NextChannelSink
{
get
{
return _nextSink;
}
}
public void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack,
object state, IMessage msg, ITransportHeaders headers, Stream stream)
{
IMethodCallMessage mc = msg as IMethodCallMessage;
if(mc!=null)
headers["X-Cache"] = this.GetMethodCacheLimit(mc.MethodBase).ToString();
sinkStack.AsyncProcessResponse(msg,headers,stream);
}
public Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack,
object state, IMessage msg, ITransportHeaders headers)
{
return null;
}
string GetMethodCacheLimit(MethodBase m)
{
foreach(CacheTimeAttribute a in m.GetCustomAttributes(typeof(CacheTimeAttribute),true))
{
return a.Time.ToString();
}
return null;
}
public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg,
ITransportHeaders requestHeaders, Stream requestStream, out IMessage responseMsg,
out ITransportHeaders responseHeaders, out Stream responseStream)
{
sinkStack.Push(this,null);
ServerProcessing res = _nextSink.ProcessMessage(sinkStack, requestMsg, requestHeaders,
requestStream, out responseMsg, out responseHeaders, out responseStream);
IMethodCallMessage mc = requestMsg as IMethodCallMessage;
if(mc!=null)
{
string t = this.GetMethodCacheLimit(mc.MethodBase);
if(t!=null)
{
if(responseHeaders==null)responseHeaders=new TransportHeaders();
responseHeaders["X-Cache"] = t;
}
}
return res;
}
}
public class CachingServerSinkProvider: IServerChannelSinkProvider
{
private IServerChannelSinkProvider _nextProvider;
public CachingServerSinkProvider(IDictionary properties, ICollection providerData)
{
}
public IServerChannelSinkProvider Next
{
get {return _nextProvider; }
set {_nextProvider = value;}
}
public IServerChannelSink CreateSink(IChannelReceiver channel)
{
IServerChannelSink next = _nextProvider.CreateSink(channel);
return new CachingServerSink(next);
}
public void GetChannelData(IChannelDataStore channelData)
{
}
}
public class CachingClientSink: BaseChannelSinkWithProperties,
IClientChannelSink
{
private IClientChannelSink _nextSink;
private static RemoteCallCache cache = new RemoteCallCache();
public CachingClientSink(IClientChannelSink next)
{
_nextSink = next;
}
public IClientChannelSink NextChannelSink
{
get
{
return _nextSink;
}
}
public void AsyncProcessRequest(IClientChannelSinkStack sinkStack,
IMessage msg, ITransportHeaders headers, Stream stream)
{
_nextSink.AsyncProcessRequest(sinkStack,msg,headers,stream);
}
public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack,
object state, ITransportHeaders headers, Stream stream)
{
sinkStack.AsyncProcessResponse(headers,stream);
}
public Stream GetRequestStream(IMessage msg,
ITransportHeaders headers)
{
return _nextSink.GetRequestStream(msg, headers);
}
[Conditional("DEBUG")]
void Trace(string msg)
{
System.Diagnostics.Trace.WriteLine(msg,"Caching");
}
public void ProcessMessage(IMessage msg, ITransportHeaders requestHeaders,
Stream requestStream, out ITransportHeaders responseHeaders, out Stream responseStream)
{
RemotingKey k = new RemotingKey(msg, requestStream, requestHeaders);
RemotingResult result = cache.GetPreviousResults(k);
if (result == null)
{
_nextSink.ProcessMessage(msg, requestHeaders, requestStream, out responseHeaders, out responseStream);
string cacheLimit = responseHeaders["X-Cache"] as string;
if (cacheLimit != null)
{
try
{
TimeSpan t = TimeSpan.Parse(cacheLimit);
Trace("Cached " + ((IMethodMessage)msg).MethodName+" till "+t.ToString());
RemotingResult r=new RemotingResult(responseStream, responseHeaders, DateTime.UtcNow + t);
cache.CacheResults(k, r);
responseStream=r.GetNewResponseStream();
}
catch{}
}
}
else
{
Trace("Use cached result for " + ((IMethodMessage)msg).MethodName);
responseStream = result.GetNewResponseStream();
responseHeaders = result.Headers;
}
}
}
[Serializable]
public class RemotingKey
{
public override int GetHashCode()
{
return hashCode;
}
public override bool Equals(object o)
{
RemotingKey ok = o as RemotingKey;
if(ok==null)return false;
if (this.key.Length != ok.key.Length)
return false;
for(int i = 0; i < key.Length; ++i)
{
if (key[i] != ok.key[i])
return false;
}
return true;
}
public RemotingKey(){}
byte[] _key=null;
public void CalculateKey()
{
if(msg==null || _key!=null)return;
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, msg);
bf.Serialize(ms, headers);
_key = ms.GetBuffer();
msg=null; headers=null;
}
public byte[] key
{
get
{
if(_key!=null)return _key;
try
{
CalculateKey();
return _key;
}
catch
{
return new byte[0];
}
}
}
int hashCode;
IMessage msg=null;
ITransportHeaders headers=null;
public RemotingKey(IMessage msg, Stream rs, ITransportHeaders headers)
{
IMethodMessage m = msg as IMethodMessage;
if(m!=null)hashCode = m.MethodName.GetHashCode();
else hashCode=0;
if(rs!=null && rs.CanSeek && rs.CanRead)
{
MemoryStream t = new MemoryStream();
if(m!=null)
{
StreamWriter s=new StreamWriter(t,Encoding.UTF8);
s.WriteLine(m.Uri);
s.WriteLine(m.TypeName);
s.WriteLine(m.MethodName);
s.Flush();
}
long op = rs.Position;
RemotingResult.CopyStream(t,rs);
rs.Seek(op,SeekOrigin.Begin);
_key = t.GetBuffer();
}
else
{
this.msg=msg;
this.headers=headers;
}
}
/*public string StringReprensentation
{
get
{
StringBuilder sb=new StringBuilder();
foreach(object e in _msg.Properties)
{
Array ee = e as Array;
if(ee==null)
{
sb.Append(e.ToString());
}
else
{
foreach(object eee in ee)
{
sb.Append(eee);
}
}
}
foreach(object e in _headers)
{
Array ee = e as Array;
if(ee==null)
{
sb.Append(e.ToString());
}
else
{
foreach(object eee in ee)
{
sb.Append(eee);
}
}
}
return sb.ToString();
/*IMethodMessage m = _msg as IMethodMessage;
if(m!=null)
{
sb.Append(m.TypeName);
sb.Append(m.MethodName);
sb.Append(m.ArgCount);
foreach(object a in m.Args)
sb.Append(a);
}
}
}*/
}
[Serializable]
public class RemotingResult
{
public static int CopyStream(Stream to, Stream from)
{
if(from.CanSeek)from.Seek(0,SeekOrigin.Begin);
byte[] buffer = new byte[256];
int size = 0;
while(true)
{
int sizeRead = from.Read(buffer,0,buffer.Length);
if(sizeRead==0)break;
to.Write(buffer,0,sizeRead);
size += sizeRead;
}
to.Seek(0,SeekOrigin.Begin);
return size;
}
public RemotingResult(Stream Response, ITransportHeaders ResponseHeaders, DateTime ValidTo)
{
CopyStream(_response,Response);
_headers = ResponseHeaders;
_validTo = ValidTo;
}
public Stream GetNewResponseStream()
{
Stream r = new MemoryStream((int)_response.Length);
CopyStream(r,_response);
return r;
}
public ITransportHeaders Headers{get{return _headers;}}
public DateTime ValidTo{get{return _validTo;}}
MemoryStream _response=new MemoryStream();
private ITransportHeaders _headers;
private DateTime _validTo;
}
public class RemoteCallCache
{
private System.Collections.Hashtable _resultMap = Hashtable.Synchronized(new Hashtable());
public RemotingResult GetPreviousResults(RemotingKey k)
{
try
{
RemotingResult res = (RemotingResult)_resultMap[k];
if (res != null && res.ValidTo < DateTime.UtcNow)
{
_resultMap.Remove(k);
return null;
}
return res;
}
catch(Exception)
{
return null;
}
}
public void CacheResults(RemotingKey k, RemotingResult Result)
{
k.CalculateKey();
_resultMap[k] = Result;
}
}
}