c#编写的一个反向代理工具,可以缓存网页到本地
proxy.ashx 主文件
using system;
using system.web;
using system.net;
using system.text;
using system.io;
using system.collections.generic;
using system.configuration;
///
/// 把http headers 和 http 响应的内容 分别存储在 /proxy/header/ 和 /proxy/body/ 中
/// 分层次创建目录
///
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);
}
}
///
/// 清理失效的缓存
///
///
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;
}
///
/// 读取缓存的header 并输出
///
///
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);
}
///
/// 输出缓存
///
/// 缓存header 的文件路径
/// 缓存 body 的文件路径
/// 是否进行判断文件过期
/// 是否输出成功
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 list = null;
if (application[applicationkey] == null)
{
application.lock();
application[applicationkey] = list = new list(1000);
application.unlock();
}
else
{
list = (list)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
~/.*$
~/ajax/.*$