A reverse proxy tool written in C # that can cache Web pages to local
PROXY.ASHX Master File
<%@ 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; //////The contents of the HTTP headers and HTTP responses are stored separately in/proxy/header/and/proxy/body////Hierarchical creation of directories///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); } } ////// cleanup of failed caches /////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; } ////// read the cached header and output ///// 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 (proxycachefol der); 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); } ////// Output cache /////File path for cache header///File path of the cache body///Whether to make a judgment file expiration///
whether the output is successful
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) {times Pan 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 ("read directly from cache failed!"); } return; } success = Echocachefile (Cacheheaderpath, Cachebodypath, true); if (success) {return; }//Update 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]; }//Determine if another process is updating the cache File if (List.contains (hash)) {success = Echocachefile (cache Headerpath, Cachebodypath, false); if (success) {return; } else {WebClient WC = new WebClient (); Wc. Headers.set ("Referer", proxyreferer); Main content byte[] data = WC. Downloaddata (Proxydomain + path); Handle 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); Main content byte[] data = WC. Downloaddata (Proxydomain + path); Handle 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 + "/hea der/"+ folder); } StreamWriter SW = File.createtext (Absfolder + "/header/" + folder + hash); Sw. Write (headers); Sw. Close (); Sw. Dispose (); Handle cached content 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 m d5 = 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/.*$ ~/proxy.ashx?cache=true&seconds=30
~/proxy.ashx?cache=false
3 >