public static void AddData(CacheEntry entry) { Monitor.Enter(_cacheLockObj); if (!_cache.Contains(entry.Key)) _cache.Add(entry.Key, entry); Monitor.Exit(_cacheLockObj); }
public static CacheEntry MakeEntry(HttpWebRequest request, HttpWebResponse response,List<Tuple<String,String>> headers, DateTime? expires) { CacheEntry newEntry = new CacheEntry(); newEntry.Expires = expires; newEntry.DateStored = DateTime.Now; newEntry.Headers = headers; newEntry.Key = new CacheKey(request.RequestUri.AbsoluteUri, request.UserAgent); newEntry.StatusCode = response.StatusCode; newEntry.StatusDescription = response.StatusDescription; if (response.ContentLength > 0) newEntry.ResponseBytes = new Byte[response.ContentLength]; return newEntry; }
private static void DoHttpProcessing(TcpClient client) { Stream clientStream = client.GetStream(); Stream outStream = clientStream; //use this stream for writing out - may change if we use ssl SslStream sslStream = null; StreamReader clientStreamReader = new StreamReader(clientStream); CacheEntry cacheEntry = null; MemoryStream cacheStream = null; if (Server.DumpHeaders || Server.DumpPostData || Server.DumpResponseData) { //make sure that things print out in order - NOTE: this is bad for performance Monitor.TryEnter(_outputLockObj, TimeSpan.FromMilliseconds(-1.0)); } try { //read the first line HTTP command String httpCmd = clientStreamReader.ReadLine(); if (String.IsNullOrEmpty(httpCmd)) { clientStreamReader.Close(); clientStream.Close(); return; } //break up the line into three components String[] splitBuffer = httpCmd.Split(spaceSplit, 3); String method = splitBuffer[0]; String remoteUri = splitBuffer[1]; Version version = new Version(1, 0); HttpWebRequest webReq; HttpWebResponse response = null; if (splitBuffer[0].ToUpper() == "CONNECT") { //Browser wants to create a secure tunnel //instead = we are going to perform a man in the middle "attack" //the user's browser should warn them of the certification errors however. //Please note: THIS IS ONLY FOR TESTING PURPOSES - you are responsible for the use of this code remoteUri = "https://" + splitBuffer[1]; while (!String.IsNullOrEmpty(clientStreamReader.ReadLine())) { ; } StreamWriter connectStreamWriter = new StreamWriter(clientStream); connectStreamWriter.WriteLine("HTTP/1.0 200 Connection established"); connectStreamWriter.WriteLine(String.Format("Timestamp: {0}", DateTime.Now.ToString())); connectStreamWriter.WriteLine("Proxy-agent: matt-dot-net"); connectStreamWriter.WriteLine(); connectStreamWriter.Flush(); sslStream = new SslStream(clientStream, false); try { sslStream.AuthenticateAsServer(_certificate, false, SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, true); } catch (Exception) { sslStream.Close(); clientStreamReader.Close(); connectStreamWriter.Close(); clientStream.Close(); return; } //HTTPS server created - we can now decrypt the client's traffic clientStream = sslStream; clientStreamReader = new StreamReader(sslStream); outStream = sslStream; //read the new http command. httpCmd = clientStreamReader.ReadLine(); if (String.IsNullOrEmpty(httpCmd)) { clientStreamReader.Close(); clientStream.Close(); sslStream.Close(); return; } splitBuffer = httpCmd.Split(spaceSplit, 3); method = splitBuffer[0]; remoteUri = remoteUri + splitBuffer[1]; } //construct the web request that we are going to issue on behalf of the client. webReq = (HttpWebRequest)HttpWebRequest.Create(remoteUri); webReq.Method = method; webReq.ProtocolVersion = version; //read the request headers from the client and copy them to our request int contentLen = ReadRequestHeaders(clientStreamReader, webReq); webReq.Proxy = null; webReq.KeepAlive = false; webReq.AllowAutoRedirect = false; webReq.AutomaticDecompression = DecompressionMethods.None; if (Server.DumpHeaders) { Console.WriteLine(String.Format("{0} {1} HTTP/{2}", webReq.Method, webReq.RequestUri.AbsoluteUri, webReq.ProtocolVersion)); DumpHeaderCollectionToConsole(webReq.Headers); } //using the completed request, check our cache if (method.ToUpper() == "GET") { cacheEntry = ProxyCache.GetData(webReq); } else if (method.ToUpper() == "POST") { char[] postBuffer = new char[contentLen]; int bytesRead; int totalBytesRead = 0; StreamWriter sw = new StreamWriter(webReq.GetRequestStream()); while (totalBytesRead < contentLen && (bytesRead = clientStreamReader.ReadBlock(postBuffer, 0, contentLen)) > 0) { totalBytesRead += bytesRead; sw.Write(postBuffer, 0, bytesRead); if (ProxyServer.Server.DumpPostData) { Console.Write(postBuffer, 0, bytesRead); } } if (Server.DumpPostData) { Console.WriteLine(); Console.WriteLine(); } sw.Close(); } if (cacheEntry == null) { //Console.WriteLine(String.Format("ThreadID: {2} Requesting {0} on behalf of client {1}", webReq.RequestUri, client.Client.RemoteEndPoint.ToString(), Thread.CurrentThread.ManagedThreadId)); webReq.Timeout = 15000; try { response = (HttpWebResponse)webReq.GetResponse(); } catch (WebException webEx) { response = webEx.Response as HttpWebResponse; } if (response != null) { List <Tuple <String, String> > responseHeaders = ProcessResponse(response); StreamWriter myResponseWriter = new StreamWriter(outStream); Stream responseStream = response.GetResponseStream(); try { //send the response status and response headers WriteResponseStatus(response.StatusCode, response.StatusDescription, myResponseWriter); WriteResponseHeaders(myResponseWriter, responseHeaders); DateTime? expires = null; CacheEntry entry = null; Boolean canCache = (sslStream == null && ProxyCache.CanCache(response.Headers, ref expires)); if (canCache) { entry = ProxyCache.MakeEntry(webReq, response, responseHeaders, expires); if (response.ContentLength > 0) { cacheStream = new MemoryStream(entry.ResponseBytes); } } Byte[] buffer; if (response.ContentLength > 0) { buffer = new Byte[response.ContentLength]; } else { buffer = new Byte[BUFFER_SIZE]; } int bytesRead; while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0) { if (cacheStream != null) { cacheStream.Write(buffer, 0, bytesRead); } outStream.Write(buffer, 0, bytesRead); if (Server.DumpResponseData) { Console.Write(UTF8Encoding.UTF8.GetString(buffer, 0, bytesRead)); } } if (Server.DumpResponseData) { Console.WriteLine(); Console.WriteLine(); } responseStream.Close(); if (cacheStream != null) { cacheStream.Flush(); cacheStream.Close(); } outStream.Flush(); if (canCache) { ProxyCache.AddData(entry); } } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { responseStream.Close(); response.Close(); myResponseWriter.Close(); } } } else { //serve from cache StreamWriter myResponseWriter = new StreamWriter(outStream); try { WriteResponseStatus(cacheEntry.StatusCode, cacheEntry.StatusDescription, myResponseWriter); WriteResponseHeaders(myResponseWriter, cacheEntry.Headers); if (cacheEntry.ResponseBytes != null) { outStream.Write(cacheEntry.ResponseBytes, 0, cacheEntry.ResponseBytes.Length); if (ProxyServer.Server.DumpResponseData) { Console.Write(UTF8Encoding.UTF8.GetString(cacheEntry.ResponseBytes)); } } myResponseWriter.Close(); } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { myResponseWriter.Close(); } } } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { if (Server.DumpHeaders || Server.DumpPostData || Server.DumpResponseData) { //release the lock Monitor.Exit(_outputLockObj); } clientStreamReader.Close(); clientStream.Close(); if (sslStream != null) { sslStream.Close(); } outStream.Close(); if (cacheStream != null) { cacheStream.Close(); } } }