欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C#实现可缓存网页到本地的反向代理工具实例

程序员文章站 2022-04-11 08:01:57
本文实例讲述了c#实现可缓存网页到本地的反向代理工具。分享给大家供大家参考。具体实现方法如下: proxy.ashx 主文件: <%@ webhandle...

本文实例讲述了c#实现可缓存网页到本地的反向代理工具。分享给大家供大家参考。具体实现方法如下:

proxy.ashx 主文件:

<%@ webhandler language="c#" class="proxy" %>
using system;
using system.web;
using system.net;
using system.text;
using system.io;
using system.collections.generic;
using system.configuration;
/// <summary>
/// 把http headers 和 http 响应的内容 分别存储在 /proxy/header/ 和 /proxy/body/ 中
/// 分层次创建目录
/// </summary>
public class proxy : ihttphandler
{
 httpresponse response;
 httprequest request;
 httpapplicationstate application;
 httpserverutility server;
 static string proxycachefolder = configurationmanager.appsettings["proxycachefolder"];
 static string proxydomain = configurationmanager.appsettings["proxydomain"];
 static string proxyreferer = configurationmanager.appsettings["proxyreferer"];
 bool proxycachedirectaccess = configurationmanager.appsettings["proxycachedirectaccess"] == "true";
 int proxycacheseconds = int.parse(configurationmanager.appsettings["proxycacheseconds"]);
 public void processrequest(httpcontext context)
 {
  response = context.response;
  request = context.request;
  application = context.application;
  server = context.server;
  string path = context.request.rawurl;
  bool delcache = path.indexof("?del") > 0;
  if (delcache)
  {
   path = path.replace("?del", string.empty);
   deletecachefile(path);
   return;
  }
  bool allowcache = request.querystring["cache"] == "true";
  string seconds = request.querystring["seconds"] ?? string.empty;
  if (!int.tryparse(seconds, out proxycacheseconds))
  {
   proxycacheseconds = 3600;
  }
  if (allowcache)
  {
   echodata(path);
  }
  else
  {
   webclient wc = new webclient();
   wc.headers.set("referer", proxyreferer);
   byte[] buffer = wc.downloaddata(proxydomain + path);
   response.contenttype = wc.responseheaders["content-type"];
   foreach (string key in wc.responseheaders.allkeys)
   {
    response.headers.set(key, wc.responseheaders[key]);
   }
   wc.dispose();
   response.outputstream.write(buffer, 0, buffer.length);
  }
 }
 /// <summary>
 /// 清理失效的缓存
 /// </summary>
 /// <param name="d"></param>
 void cleartimeoutcache(directoryinfo d)
 {
  if (d.exists)
  {
   fileinfo[] files = d.getfiles();
   foreach (fileinfo file in files)
   {
    timespan timespan = datetime.now - file.lastaccesstime;
    if (timespan.totalseconds > proxycacheseconds)
    {
     file.delete();
    }
   }
  }
 }
 string getcachefolderpath(string hash)
 {
  string s = string.empty;
  for (int i = 0; i <= 2; i++)
  {
   s += hash[i] + "/";
  }
  return s;
 }
 /// <summary>
 /// 读取缓存的header 并输出
 /// </summary>
 /// <param name="cacheheaderpath"></param>
 void echocacheheader(string cacheheaderpath)
 {
  string[] headers = file.readalllines(cacheheaderpath);
  for (int i = 0; i < headers.length; i++)
  {
   string[] headerkeyvalue = headers[i].split(':');
   if (headerkeyvalue.length == 2)
   {
    if (headerkeyvalue[0] == "content-type")
    {
     response.contenttype = headerkeyvalue[1];
    }
    response.headers.set(headerkeyvalue[0], headerkeyvalue[1]);
   }
  }
 }
 void deletecachefile(string path)
 {
  string absfolder = server.mappath(proxycachefolder);
  string hash = gethashstring(path);
  string folder = getcachefolderpath(hash);
  string cachebodypath = absfolder + "/body/" + folder + hash;
  string cacheheaderpath = absfolder + "/header/" + folder + hash;
  fileinfo cachebody = new fileinfo(cachebodypath);
  fileinfo cacheheader = new fileinfo(cacheheaderpath);
  if (cachebody.exists)
  {
   cachebody.delete();
  }
  if (cacheheader.exists)
  {
   cacheheader.delete();
  }
  response.write("delete cache file success!\r\n" + path);
 }
 /// <summary>
 /// 输出缓存
 /// </summary>
 /// <param name="cacheheaderpath">缓存header 的文件路径</param>
 /// <param name="cachebodypath">缓存 body 的文件路径</param>
 /// <param name="iftimeout">是否进行判断文件过期</param>
 /// <returns>是否输出成功</returns>
 bool echocachefile(string cacheheaderpath, string cachebodypath, bool iftimeout)
 {
  fileinfo cachebody = new fileinfo(cachebodypath);
  fileinfo cacheheader = new fileinfo(cacheheaderpath);
  cleartimeoutcache(cachebody.directory);
  cleartimeoutcache(cacheheader.directory);
  if (cachebody.exists && cacheheader.exists)
  {
   if (iftimeout)
   {
    timespan timespan = datetime.now - cachebody.lastwritetime;
    if (timespan.totalseconds < proxycacheseconds)
    {
     echocacheheader(cacheheaderpath);
     response.transmitfile(cachebodypath);
     return true;
    }
   }
   else
   {
    echocacheheader(cacheheaderpath);
    response.transmitfile(cachebodypath);
    return true;
   }
  }
  return false;
 }
 void echodata(string path)
 {
  string absfolder = server.mappath(proxycachefolder);
  string hash = gethashstring(path);
  string folder = getcachefolderpath(hash);
  string cachebodypath = absfolder + "/body/" + folder + hash;
  string cacheheaderpath = absfolder + "/header/" + folder + hash;
  bool success;
  if (proxycachedirectaccess)
  {
   success = echocachefile(cacheheaderpath, cachebodypath, false);
   if (!success)
   {
    response.write("直接从缓存读取失败!");
   }
   return;
  }
  success = echocachefile(cacheheaderpath, cachebodypath, true);
  if (success)
  {
   return;
  }
  //更新cache file
  string applicationkey = "cachelist";
  list<string> list = null;
  if (application[applicationkey] == null)
  {   
   application.lock();
   application[applicationkey] = list = new list<string>(1000);
   application.unlock();
  }
  else
  {
   list = (list<string>)application[applicationkey];
  }
  //判断是否已有另一个进程正在更新cache file
  if (list.contains(hash))
  {
   success = echocachefile(cacheheaderpath, cachebodypath, false);
   if (success)
   {
    return;
   }
   else
   {
    webclient wc = new webclient();
    wc.headers.set("referer", proxyreferer);
    //主体内容
    byte[] data = wc.downloaddata(proxydomain + path);
    //处理header
    response.contenttype = wc.responseheaders["content-type"];
    foreach (string key in wc.responseheaders.allkeys)
    {
     response.headers.set(key, wc.responseheaders[key]);
    }
    wc.dispose();
    response.binarywrite(data);
   }
  }
  else
  {
   webclient wc = new webclient();
   wc.headers.set("referer", proxyreferer);
   stringbuilder headersb = new stringbuilder();
   list.add(hash);
   //主体内容
   byte[] data = wc.downloaddata(proxydomain + path);
   //处理header
   response.contenttype = wc.responseheaders["content-type"];
   foreach (string key in wc.responseheaders.allkeys)
   {
    headersb.append(key);
    headersb.append(":");
    headersb.append(wc.responseheaders[key]);
    headersb.append("\r\n");
    response.headers.set(key, wc.responseheaders[key]);
   }
   wc.dispose();
   string headers = headersb.tostring().trim();
   if (!directory.exists(absfolder + "/header/" + folder))
   {
    directory.createdirectory(absfolder + "/header/" + folder);
   }
   streamwriter sw = file.createtext(absfolder + "/header/" + folder + hash);
   sw.write(headers);
   sw.close();
   sw.dispose();
   //处理缓存内容
   if (!directory.exists(absfolder + "/body/" + folder))
   {
    directory.createdirectory(absfolder + "/body/" + folder);
   }
   filestream fs = file.create(absfolder + "/body/" + folder + hash);
   fs.write(data, 0, data.length);
   fs.close();
   fs.dispose();
   list.remove(hash);
   response.binarywrite(data);
  }
 }
 string gethashstring(string path)
 {
  string md5 = getmd5str(path);
  return md5;
 }
 static string getmd5str(string convertstring)
 {
  system.security.cryptography.md5cryptoserviceprovider md5 = new system.security.cryptography.md5cryptoserviceprovider();
  string t2 = bitconverter.tostring(md5.computehash(utf8encoding.default.getbytes(convertstring)), 4, 8);
  t2 = t2.replace("-", "");
  return t2;
 }
 public bool isreusable
 {
  get
  {
   return false;
  }
 }
}

web.config文件如下:

<?xml version="1.0"?>
<configuration>
 <configsections>
 <section name="rewriterconfig" type="urlrewriter.config.rewriterconfigserializersectionhandler, urlrewriter"/> 
 </configsections>
 <rewriterconfig>
 <rules>
  <rewriterrule>
  <lookfor>~/.*$</lookfor>
  <sendto>
  <!--cache=true 设置此路径进行缓存-->
   <![cdata[~/proxy.ashx?cache=true&seconds=30]]>
  </sendto>
  </rewriterrule>
  <rewriterrule>
  <lookfor>~/ajax/.*$</lookfor>
  <sendto>
  <!--cache=false 设置此路径不允许缓存-->
   <![cdata[~/proxy.ashx?cache=false]]>
  </sendto>
  </rewriterrule>
 </rules>
 </rewriterconfig>
 <appsettings>
 <!--#反向代理设置 start-->
 <!--设置站点-->
 <add key="proxydomain" value="http://127.0.0.1:12123/"/>
 <!--缓存文件夹-->
 <add key="proxycachefolder" value="/proxycache/"/>
 <!--缓存时长-->
 <add key="proxycacheseconds" value="3600"/>
 <!--设置不再判断缓存文件是否超时,直接从缓存读取-->
 <add key="proxycachedirectaccess" value="false"/>
 <!--设置反向代理referer-->
 <add key="proxyreferer" value="http://www.www.com/"/>
 <!--#反向代理设置 end-->
 </appsettings>
 <system.webserver>
 <modules runallmanagedmodulesforallrequests="true">
  <add type="urlrewriter.modulerewriter, urlrewriter" name="modulerewriter"/>
 </modules>
 </system.webserver>
 <system.web>
 <compilation debug="true"/>
 </system.web>
</configuration>

希望本文所述对大家的c#程序设计有所帮助。