New test.
[mono.git] / mcs / class / System.Web / System.Web / HttpResponse.cs
index d12c55dead2556cbe3062161e78d7305c63d8d74..890078fb4a179bffa4567190965a291a6c06bc51 100644 (file)
@@ -1,13 +1,12 @@
-// 
-// System.Web.HttpResponse
 //
-// Authors:
-//     Patrik Torstensson (Patrik.Torstensson@labs2.com)
-//     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+// System.Web.HttpResponse.cs 
 //
-// (c) 2002 Ximian, Inc. (http://www.ximian.com)
+// 
+// Author:
+//     Miguel de Icaza (miguel@novell.com)
+//     Gonzalo Paniagua Javier (gonzalo@ximian.com)
 //
-
+// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-using System;
+
+using System.Text;
+using System.Web.UI;
 using System.Collections;
-using System.Globalization;
+using System.Collections.Specialized;
 using System.IO;
-using System.Text;
+using System.Web.Caching;
 using System.Threading;
 using System.Web.Util;
-using System.Web.Caching;
-
-namespace System.Web
-{
-       public sealed class HttpResponse
-       {
-               // Chunked encoding static helpers
-               static byte [] s_arrChunkSuffix = {13, 10};
-               static byte [] s_arrChunkEnd = {48, 13, 10, 13, 10};
-               static string s_sChunkedPrefix = "\r\n";
+using System.Globalization;
+using System.Security.Permissions;
 
-               ArrayList _Headers;
-                       
-               bool _bClientDisconnected;
-               bool _bSuppressHeaders;
-               bool _bSuppressContent;
-               bool _bChunked;
-               bool last_chunk_sent;
-               bool _bEnded;
-               bool _bBuffering;
-               bool _bHeadersSent;
-               bool _bFlushing;
-               bool filtered;
-               long _lContentLength;
-               int _iStatusCode;
+namespace System.Web {
+       
+       // CAS - no InheritanceDemand here as the class is sealed
+       [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
+       public sealed class HttpResponse {
+               internal HttpWorkerRequest WorkerRequest;
+               internal HttpResponseStream output_stream;
+               internal bool buffer = true;
                
-               int _expiresInMinutes;
-               bool _expiresInMinutesSet;
-               DateTime _expiresAbsolute;
-               bool _expiresAbsoluteSet;
-
-               bool closed;
-
-               string  _sContentType;
-               string  _sCacheControl;
-               string  _sTransferEncoding;
-               string  _sCharset;
-               string  _sStatusDescription;
-               bool forced_charset;
-
-               HttpCookieCollection _Cookies;
-               HttpCachePolicy _CachePolicy;
-
-               Encoding _ContentEncoding;
-                       
-               HttpContext _Context;
-               HttpWriter _Writer;
-               TextWriter _TextWriter;
-
-               HttpWorkerRequest _WorkerRequest;
+               HttpContext context;
+               TextWriter writer;
+               HttpCachePolicy cache_policy;
+               Encoding encoding;
+               HttpCookieCollection cookies;
+               
+               int status_code = 200;
+               string status_description = "OK";
 
-               ArrayList fileDependencies;
+               string content_type = "text/html";
+               string charset;
+               bool charset_set;
                CachedRawResponse cached_response;
+               string user_cache_control = "private";
+               string redirect_location;
+               
+               //
+               // Negative Content-Length means we auto-compute the size of content-length
+               // can be overwritten with AppendHeader ("Content-Length", value)
+               //
+               long content_length = -1;
+
+               //
+               // The list of the headers that we will send back to the client, except
+               // the headers that we compute here.
+               //
+               ArrayList headers = new ArrayList ();
+               bool headers_sent;
                ArrayList cached_headers;
-#if NET_1_1
-               string redirectLocation;
-#endif
 
-               string app_path_mod = null;
-               private HttpResponse ()
-               {
-                        _bBuffering = true;
-                        _Headers = new ArrayList ();
-                        _sContentType = "text/html";
-                        _iStatusCode = 200;
-               }
+               //
+               // Transfer encoding state
+               //
+               string transfer_encoding;
+               internal bool use_chunked;
+               
+               bool closed;
+               internal bool suppress_content;
 
-               public HttpResponse (TextWriter output) : this ()
-               {
-                        _TextWriter = output;
-               }
+               //
+               // Session State
+               //
+               string app_path_mod;
+               
+               //
+               // Passed as flags
+               //
+               internal object FlagEnd = new object ();
 
-               internal HttpResponse (HttpWorkerRequest WorkerRequest, HttpContext Context) : this ()
+               internal HttpResponse ()
                {
-                        _Context = Context;
-                        _WorkerRequest = WorkerRequest;
+                       output_stream = new HttpResponseStream (this);
                }
 
-#if TARGET_J2EE
-       public HttpWorkerRequest WorkerRequest{
-               get{ return _WorkerRequest; }
-       }
-#endif
-
-               internal void InitializeWriter ()
-               {
-                       // We cannot do this in the .ctor because HttpWriter uses configuration and
-                       // it may not be initialized
-                       if (_Writer == null) {
-                                _Writer = new HttpWriter (this);
-                                _TextWriter = _Writer;
-                       }
-               }
-               
-               internal void FinalFlush ()
+               public HttpResponse (TextWriter writer) : this ()
                {
-                       Flush (true);
+                       this.writer = writer;
                }
 
-               internal void DoFilter (bool really)
+               internal HttpResponse (HttpWorkerRequest worker_request, HttpContext context) : this ()
                {
-                       if (really && null != _Writer) 
-                               _Writer.FilterData (true);
+                       WorkerRequest = worker_request;
+                       this.context = context;
 
-                       filtered = true;
-               }
-
-               internal bool IsCached {
-                       get { return cached_response != null; }
-               }
-
-               internal CachedRawResponse GetCachedResponse () {
-                       cached_response.StatusCode = StatusCode;
-                       cached_response.StatusDescription = StatusDescription;
-                       return cached_response;
-               }
-
-               internal void SetCachedHeaders (ArrayList headers)
-               {
-                       cached_headers = headers;
+                       if (worker_request != null)
+                               use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1");
                }
                
-               private ArrayList GenerateHeaders ()
+               internal TextWriter SetTextWriter (TextWriter writer)
                {
-                       ArrayList oHeaders = new ArrayList (_Headers);
-
-                       oHeaders.Add (new HttpResponseHeader ("X-Powered-By", "Mono"));
-
-#if !TARGET_J2EE
-                       string date = DateTime.UtcNow.ToString ("ddd, d MMM yyyy HH:mm:ss 'GMT'", CultureInfo.InvariantCulture);
-                       HttpResponseHeader date_header = new HttpResponseHeader ("Date", date);
-                       oHeaders.Add (date_header);
+                       TextWriter prev = writer;
                        
-                       if (IsCached)
-                               cached_response.DateHeader = date_header;
-
-                       if (_lContentLength > 0) {
-                               oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderContentLength,
-                                                                     _lContentLength.ToString ()));
-                       }
-#endif
-
-                       // Apache2 only auto-adds 'charset=blah' for text/plain and text/html
-                       if (_sContentType != null) {
-                               string ctype = _sContentType;
-                               if (forced_charset || _sContentType == "text/plain" || _sContentType == "text/html") {
-                                       if (_sContentType.IndexOf ("charset=") == -1) {
-                                               if (Charset.Length == 0)
-                                                       Charset = ContentEncoding.HeaderName;
-
-                                               // Time to build our string
-                                               if (Charset.Length > 0)
-                                                       ctype += "; charset=" + Charset;
-                                       }
-                               }
-
-                               oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderContentType, ctype));
-                       }
-
-                       if (_CachePolicy != null)
-                               _CachePolicy.SetHeaders (this, oHeaders);
+                       this.writer = writer;
                        
-                       if (_sCacheControl != null) {
-                               oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderPragma,
-                                                                     _sCacheControl));
-                       }
-
-                       if (_sTransferEncoding != null) {
-                               oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderTransferEncoding,
-                                                                     _sTransferEncoding));
-                       }
-
-                       if (_Cookies != null) {
-                               int length = _Cookies.Count;
-                               for (int i = 0; i < length; i++) {
-                                       oHeaders.Add (_Cookies.Get (i).GetCookieHeader ());
-                               }
-                       }
-#if NET_1_1
-                       if (redirectLocation != null)
-                               oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderLocation,
-                                                                     redirectLocation));
-#endif
-                       return oHeaders;
+                       return prev;
                }
                
-               private void SendHeaders ()
-               {
-                       _WorkerRequest.SendStatus (StatusCode, StatusDescription);
-                       
-                       ArrayList oHeaders;
-
-                       if (cached_headers != null)
-                               oHeaders = cached_headers;
-                       else
-                               oHeaders = GenerateHeaders ();
-
-                       if (cached_response != null)
-                               cached_response.SetHeaders (oHeaders);
-                       
-                       foreach (HttpResponseHeader oHeader in oHeaders)
-                               oHeader.SendContent (_WorkerRequest);
-                       
-#if !TARGET_J2EE //in J2EE env. we are setting headers into
-                //HttpServletResponse instance -> headers are
-                //still not sent
-                       _bHeadersSent = true;
-#endif
-               }
-
-               public string Status
-               {
+               public bool Buffer {
                        get {
-                               return String.Format ("{0} {1}", StatusCode, StatusDescription);
+                               return buffer;
                        }
 
                        set {
-                               string sMsg = "OK";
-                               int iCode = 200;
-
-                               try {
-                                       iCode = Int32.Parse (value.Substring (0, value.IndexOf (' ')));
-                                       sMsg = value.Substring (value.IndexOf (' ') + 1);
-                               } catch (Exception) {
-                                       throw new HttpException ("Invalid status string");
-                               }
-
-                               StatusCode = iCode;
-                               StatusDescription = sMsg;
+                               buffer = value;
                        }
                }
 
-               [MonoTODO()]
-               public void AddCacheItemDependencies (ArrayList cacheKeys)
-               {
-                       throw new NotImplementedException ();
-               }
-
-               [MonoTODO()]
-               public void AddCacheItemDependency(string cacheKey)
-               {
-                       throw new NotImplementedException ();
-               }
-
-               public void AddFileDependencies (ArrayList filenames)
-               {
-                       if (filenames == null || filenames.Count == 0)
-                               return;
-                       
-                       if (fileDependencies == null) {
-                               fileDependencies = (ArrayList) filenames.Clone ();
-                               return;
+               public bool BufferOutput {
+                       get {
+                               return buffer;
                        }
 
-                       foreach (string fn in filenames)
-                               AddFileDependency (fn);
-               }
-
-               public void AddFileDependency (string filename)
-               {
-                       if (fileDependencies == null)
-                               fileDependencies = new ArrayList ();
-
-                       fileDependencies.Add (filename);
-               }
-
-               public void AddHeader (string name, string value)
-               {
-                       AppendHeader(name, value);
-               }
-
-               public void AppendCookie (HttpCookie cookie)
-               {
-                       if (_bHeadersSent)
-                               throw new HttpException ("Cannot append cookies after HTTP headers have been sent");
-
-                       Cookies.Add (cookie);
-               }
-
-               [MonoTODO()]
-               public void AppendToLog (string param)
-               {
-                       throw new NotImplementedException ();
+                       set {
+                               buffer = value;
+                       }
                }
 
-               public string ApplyAppPathModifier (string virtualPath)
-               {
-                       if (virtualPath == null)
-                               return null;
-
-                       if (virtualPath == "")
-                               return _Context.Request.RootVirtualDir;
-
-                       if (UrlUtils.IsRelativeUrl (virtualPath)) {
-                               virtualPath = UrlUtils.Combine (_Context.Request.RootVirtualDir, virtualPath);
-                       } else if (UrlUtils.IsRooted (virtualPath)) {
-                               virtualPath = UrlUtils.Reduce (virtualPath);
+               //
+               // Use the default from <globalization> section if the client has not set the encoding
+               //
+               public Encoding ContentEncoding {
+                       get {
+                               if (encoding == null) {
+                                       if (context != null) {
+                                               string client_content_type = context.Request.ContentType;
+                                               string parameter = HttpRequest.GetParameter (client_content_type, "; charset=");
+                                               if (parameter != null) {
+                                                       try {
+                                                               // Do what the #1 web server does
+                                                               encoding = Encoding.GetEncoding (parameter);
+                                                       } catch {
+                                                       }
+                                               }
+                                       }
+                                       if (encoding == null)
+                                               encoding = WebEncoding.ResponseEncoding;
+                               }
+                               return encoding;
                        }
 
-                       if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) < 0) {
-                               string rvd = _Context.Request.RootVirtualDir;
-                               string basevd = rvd.Replace (app_path_mod, "");
-
-                               if (!StrUtils.StartsWith (virtualPath, basevd))
-                                       return virtualPath;
+                       set {
+                               if (value == null)
+                                       throw new ArgumentException ("ContentEncoding can not be null");
 
-                               virtualPath = UrlUtils.Combine (rvd, virtualPath.Substring (basevd.Length));
+                               encoding = value;
+                               HttpWriter http_writer = writer as HttpWriter;
+                               if (http_writer != null)
+                                       http_writer.SetEncoding (encoding);
                        }
-
-                       return virtualPath;
-               }
-
-               internal void SetAppPathModifier (string app_path_mod)
-               {
-                       this.app_path_mod = app_path_mod;
                }
                
-               public bool Buffer
-               {
+               public string ContentType {
                        get {
-                               return BufferOutput;
+                               return content_type;
                        }
 
                        set {
-                               BufferOutput = value;
+                               content_type = value;
                        }
                }
 
-               public bool BufferOutput
-               {
+               public string Charset {
                        get {
-                               return _bBuffering;
+                               if (charset == null)
+                                       charset = ContentEncoding.WebName;
+                               
+                               return charset;
                        }
-                       
-                       set {
-                               if (_Writer != null)
-                                       _Writer.Update ();
 
-                               _bBuffering = value;
+                       set {
+                               charset_set = true;
+                               charset = value;
                        }
                }
-
-               public HttpCachePolicy Cache
-               {
+               
+               public HttpCookieCollection Cookies {
                        get {
-                               if (null == _CachePolicy) {
-                                       _CachePolicy = new HttpCachePolicy ();
-                                       _CachePolicy.CacheabilityUpdated += new CacheabilityUpdatedCallback (
-                                               OnCacheabilityUpdated);
-                               }
-
-                               return _CachePolicy;
+                               if (cookies == null)
+                                       cookies = new HttpCookieCollection (true, false);
+                               return cookies;
                        }
                }
-
-               private void OnCacheabilityUpdated (object sender, CacheabilityUpdatedEventArgs e)
-               {
-                       if (e.Cacheability >= HttpCacheability.Server && !IsCached)
-                               cached_response = new CachedRawResponse (_CachePolicy);
-                       else if (e.Cacheability <= HttpCacheability.Private)
-                               cached_response = null;
-               }
                
-               [MonoTODO("Set status in the cache policy")]
-               public string CacheControl
-               {
+               public int Expires {
                        get {
-                               return _sCacheControl;
+                               if (cache_policy == null)
+                                       return 0;
+
+                               return cache_policy.ExpireMinutes ();
                        }
 
                        set {
-                               if (_bHeadersSent)
-                                       throw new HttpException ("Headers has been sent to the client");
-
-                               _sCacheControl = value;
+                               Cache.SetExpires (DateTime.Now + new TimeSpan (0, value, 0));
                        }
                }
-
-               public string Charset
-               {
+               
+               public DateTime ExpiresAbsolute {
                        get {
-                               if (null == _sCharset)
-                                       _sCharset = ContentEncoding.WebName;
-
-                               return _sCharset;
+                               return Cache.Expires;
                        }
 
                        set {
-                               if (_bHeadersSent)
-                                       throw new HttpException ("Headers has been sent to the client");
-
-                               if (value == null)
-                                       value = "";
-
-                               forced_charset = true;
-                               _sCharset = value;
+                               Cache.SetExpires (value);
                        }
                }
 
-               public Encoding ContentEncoding
-               {
+               public Stream Filter {
                        get {
-                               if (_ContentEncoding == null)
-                                       _ContentEncoding = WebEncoding.ResponseEncoding;
+                               if (WorkerRequest == null)
+                                       return null;
 
-                               return _ContentEncoding;
+                               return output_stream.Filter;
                        }
 
+                       set {
+                               output_stream.Filter = value;
+                       }
+               }
+#if NET_2_0
+               [MonoTODO]
+               public Encoding HeaderEncoding {
+                       get { throw new NotImplementedException (); }
                        set {
                                if (value == null)
-                                       throw new ArgumentNullException ("Can't set a null as encoding");
-
-                               _ContentEncoding = value;
-
-                               if (_Writer != null)
-                                       _Writer.Update ();
+                                       throw new ArgumentNullException ("HeaderEncoding");
+                               throw new NotImplementedException ();
                        }
                }
-
-               public string ContentType
-               {
+#endif
+               public bool IsClientConnected {
                        get {
-                               return _sContentType;
-                       }
+                               if (WorkerRequest == null)
+                                       return true; // yep that's true
 
-                       set {
-                               if (_bHeadersSent)
-                                       throw new HttpException ("Headers has been sent to the client");
-
-                               _sContentType = value;
+                               return WorkerRequest.IsClientConnected ();
                        }
                }
-
-               public HttpCookieCollection Cookies
-               {
+#if NET_2_0
+               [MonoTODO]
+               public bool IsRequestBeingRedirected {
+                       get { throw new NotImplementedException (); }
+               }
+#endif
+               public TextWriter Output {
                        get {
-                               if (null == _Cookies)
-                                       _Cookies = new HttpCookieCollection (this, false);
+                               if (writer == null)
+                                       writer = new HttpWriter (this);
 
-                               return _Cookies;
+                               return writer;
                        }
                }
 
-               public int Expires
-               {
+               public Stream OutputStream {
                        get {
-                               return _expiresInMinutes;
+                               return output_stream;
+                       }
+               }
+               
+               public string RedirectLocation {
+                       get {
+                               return redirect_location;
                        }
 
                        set {
-                               if (!_expiresInMinutesSet || (value < _expiresInMinutes))
-                               {
-                                       _expiresInMinutes = value;
-                                       Cache.SetExpires(_Context.Timestamp.Add(new TimeSpan(0, _expiresInMinutes, 0)));
-                               }
-                               _expiresInMinutesSet = true;
+                               redirect_location = value;
                        }
                }
-
-               public DateTime ExpiresAbsolute
-               {
+               
+               public string Status {
                        get {
-                               return _expiresAbsolute;
+                               return String.Format ("{0} {1}", status_code, StatusDescription);
                        }
 
                        set {
-                               if (!_expiresAbsoluteSet || value.CompareTo(_expiresAbsolute)<0)
-                               {
-                                       _expiresAbsolute = value;
-                                       Cache.SetExpires(_expiresAbsolute); 
+                               int p = value.IndexOf (' ');
+                               if (p == -1)
+                                       throw new HttpException ("Invalid format for the Status property");
+
+                               string s = value.Substring (0, p);
+                               
+#if NET_2_0
+                               if (!Int32.TryParse (s, out status_code))
+                                       throw new HttpException ("Invalid format for the Status property");
+#else
+                                                   
+                               try {
+                                       status_code = Int32.Parse (s);
+                               } catch {
+                                       throw new HttpException ("Invalid format for the Status property");
                                }
-                               _expiresAbsoluteSet = true;
+#endif
+                               
+                               status_description = value.Substring (p+1);
                        }
                }
 
-               public Stream Filter
-               {
+               public int StatusCode {
                        get {
-                               if (_Writer != null)
-                                       return _Writer.GetActiveFilter ();
-
-                               return null;
+                               return status_code;
                        }
 
                        set {
-                               if (_Writer == null)
-                                       throw new HttpException ("Filtering is not allowed");
-
-                               _Writer.ActivateFilter (value);
+                               if (headers_sent)
+                                       throw new HttpException ("headers have already been sent");
+                               
+                               status_code = value;
+                               status_description = null;
                        }
                }
 
-               public bool IsClientConnected {
+               public string StatusDescription {
                        get {
-                               if (_bClientDisconnected)
-                                       return false;
+                               if (status_description == null)
+                                       status_description = HttpWorkerRequest.GetStatusDescription (status_code);
 
-                               if (null != _WorkerRequest)
-                                       _bClientDisconnected = (!_WorkerRequest.IsClientConnected ());
+                               return status_description;
+                       }
 
-                               return !_bClientDisconnected;
+                       set {
+                               if (headers_sent)
+                                       throw new HttpException ("headers have already been sent");
+                               
+                               status_description = value;
                        }
                }
-      
-               public TextWriter Output
-               {
+               
+               public bool SuppressContent {
                        get {
-                               return _TextWriter;
+                               return suppress_content;
                        }
-               }
-
-               public Stream OutputStream
-               {
-                       get {
-                               if (_Writer == null)
-                                       throw new HttpException ("an Output stream not available when " +
-                                                                "running with custom text writer");
 
-                               return _Writer.OutputStream;
+                       set {
+                               suppress_content = value;
                        }
                }
+#if NET_2_0
+               [MonoTODO]
+               public void AddCacheDependency (CacheDependency[] dependencies)
+               {
+                       throw new NotImplementedException ();
+               }
 
-#if NET_1_1
-               public string RedirectLocation {
-                       get { return redirectLocation; }
-                       set { redirectLocation = value; }
+               [MonoTODO]
+               public void AddCacheItemDependencies (string[] cacheKeys)
+               {
+                       throw new NotImplementedException ();
                }
 #endif
-               
-               public string StatusDescription
+               [MonoTODO]
+               public void AddCacheItemDependencies (ArrayList cacheKeys)
                {
-                       get {
-                               if (null == _sStatusDescription)
-                                       _sStatusDescription =
-                                               HttpWorkerRequest.GetStatusDescription (_iStatusCode);
-
-                               return _sStatusDescription;
-                       }
+                       // TODO: talk to jackson about the cache
+               }
 
-                       set {
-                               if (_bHeadersSent)
-                                       throw new HttpException ("Headers has been sent to the client");
+               [MonoTODO]
+               public void AddCacheItemDependency (string cacheKey)
+               {
+                       // TODO: talk to jackson about the cache
+               }
 
-                               _sStatusDescription = value;
-                       }
+               [MonoTODO]
+               public void AddFileDependencies (ArrayList filenames)
+               {
+                       // TODO: talk to jackson about the cache
                }
-       
-               public int StatusCode
+#if NET_2_0
+               [MonoTODO]
+               public void AddFileDependencies (string[] filenames)
                {
-                       get {
-                               return _iStatusCode;
-                       }
+                       throw new NotImplementedException ();
+               }
+#endif
+               [MonoTODO]
+               public void AddFileDependency (string filename)
+               {
+                       // TODO: talk to jackson about the cache
+               }
 
-                       set {
-                               if (_bHeadersSent)
-                                       throw new HttpException ("Headers has been sent to the client");
+               public void AddHeader (string name, string value)
+               {
+                       AppendHeader (name, value);
+               }
 
-                               _sStatusDescription = null;
-                               _iStatusCode = value;
-                       }
+               public void AppendCookie (HttpCookie cookie)
+               {
+                       Cookies.Add (cookie);
                }
 
-               public bool SuppressContent
+               //
+               // AppendHeader:
+               //    Special case for Content-Length, Content-Type, Transfer-Encoding and Cache-Control
+               //
+               //
+               public void AppendHeader (string name, string value)
                {
-                       get {
-                               return _bSuppressContent;
-                       }
+                       if (headers_sent)
+                               throw new HttpException ("headers have been already sent");
                        
-                       set {
-                               _bSuppressContent = true;
+                       if (String.Compare (name, "content-length", true, CultureInfo.InvariantCulture) == 0){
+                               content_length = (long) UInt64.Parse (value);
+                               use_chunked = false;
+                               return;
                        }
-               }
 
-               HttpRequest Request
-               {
-                       get {
-                               if (_Context == null)
-                                       return null;
+                       if (String.Compare (name, "content-type", true, CultureInfo.InvariantCulture) == 0){
+                               ContentType = value;
+                               return;
+                       }
 
-                               return _Context.Request;
+                       if (String.Compare (name, "transfer-encoding", true, CultureInfo.InvariantCulture) == 0){
+                               transfer_encoding = value;
+                               use_chunked = false;
+                               return;
                        }
-               }
 
-               internal void AppendHeader (int iIndex, string value)
-               {
-                       if (_bHeadersSent)
-                               throw new HttpException ("Headers has been sent to the client");
-
-                       switch (iIndex) {
-                       case HttpWorkerRequest.HeaderContentLength:
-                               _lContentLength = Int64.Parse (value);
-                               break;
-                       case HttpWorkerRequest.HeaderContentEncoding:
-                               _sContentType = value;
-                               break;
-                       case HttpWorkerRequest.HeaderTransferEncoding:
-                               _sTransferEncoding = value;
-                               _bChunked = (value == "chunked");
-                               break;
-                       case HttpWorkerRequest.HeaderPragma:
-                               _sCacheControl = value;
-                               break;
-                       default:
-                               _Headers.Add (new HttpResponseHeader (iIndex, value));
-                               break;
+                       if (String.Compare (name, "cache-control", true, CultureInfo.InvariantCulture) == 0){
+                               user_cache_control = value;
+                               return;
                        }
+
+                       headers.Add (new UnknownResponseHeader (name, value));
                }
 
-               public void AppendHeader (string name, string value)
+               [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
+               public void AppendToLog (string param)
                {
-                       if (_bHeadersSent)
-                               throw new HttpException ("Headers has been sent to the client");
-
-                       switch (name.ToLower (CultureInfo.InvariantCulture)) {
-                       case "content-length":
-                               _lContentLength = Int64.Parse (value);
-                               break;
-                       case "content-type":
-                               _sContentType = value;
-                               break;
-                       case "transfer-encoding":
-                               _sTransferEncoding = value;
-                               _bChunked = (value == "chunked");
-                               break;
-                       case "pragma":
-                               _sCacheControl = value;
-                               break;
-                       default:
-                               _Headers.Add (new HttpResponseHeader (name, value));
-                               break;
-                       }
+                       Console.Write ("System.Web: ");
+                       Console.WriteLine (param);
                }
-       
-               internal TextWriter SetTextWriter (TextWriter w)
+               
+               public string ApplyAppPathModifier (string virtualPath)
                {
-                       TextWriter prev = _TextWriter;
-                       _TextWriter = w;
-                       return prev;
-               }
+                       if (virtualPath == null)
+                               return null;
+               
+                       if (virtualPath == "")
+                               return context.Request.RootVirtualDir;
+               
+                       if (UrlUtils.IsRelativeUrl (virtualPath)) {
+                               virtualPath = UrlUtils.Combine (context.Request.RootVirtualDir, virtualPath);
+                       } else if (UrlUtils.IsRooted (virtualPath)) {
+                               virtualPath = UrlUtils.Canonic (virtualPath);
+                       }
+               
+                       if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) < 0) {
+                               string rvd = context.Request.RootVirtualDir;
+                               string basevd = rvd.Replace (app_path_mod, "");
+               
+                               if (!StrUtils.StartsWith (virtualPath, basevd))
+                                       return virtualPath;
+               
+                               virtualPath = UrlUtils.Combine (rvd, virtualPath.Substring (basevd.Length));
+                       }
                
+                       return virtualPath;
+               }
+
                public void BinaryWrite (byte [] buffer)
                {
-                       OutputStream.Write (buffer, 0, buffer.Length);
+                       output_stream.Write (buffer, 0, buffer.Length);
                }
 
-               internal void BinaryWrite (byte [] buffer, int start, int length)
+               internal void BinaryWrite (byte [] buffer, int start, int len)
                {
-                       OutputStream.Write (buffer, start, length);
+                       output_stream.Write (buffer, start, len);
                }
-               
+
                public void Clear ()
                {
-                       if (_Writer != null)
-                               _Writer.Clear ();
+                       ClearContent ();
                }
 
                public void ClearContent ()
                {
-                       Clear();
+                       output_stream.Clear ();
                }
 
-               internal void SetHeadersSent (bool val)
-               {
-                       _bHeadersSent = val;
-               }
-               
                public void ClearHeaders ()
                {
-                       if (_bHeadersSent)
-                               throw new HttpException ("Headers has been sent to the client");
+                       if (headers_sent)
+                               throw new HttpException ("headers have been already sent");
 
-                       _sContentType = "text/html";
-                       forced_charset = false;
-
-                       _iStatusCode = 200;
-                       _sCharset = null;
-                       _Headers.Clear ();
-                       _sCacheControl = null;
-                       _sTransferEncoding = null;
+                       // Reset the special case headers.
+                       content_length = -1;
+                       content_type = "text/html";
+                       transfer_encoding = null;
+                       user_cache_control = null;
+                       headers.Clear ();
+               }
 
-                       _lContentLength = 0;
-                       _bSuppressContent = false;
-                       _bSuppressHeaders = false;
-                       _bClientDisconnected = false;
+               internal bool HeadersSent {
+                       get {
+                               return headers_sent;
+                       }
                }
 
                public void Close ()
                {
-                       if (!closed && !_bClientDisconnected) {
-                               if (_bChunked && !last_chunk_sent) {
-                                       last_chunk_sent = true;
-                                       _WorkerRequest.SendResponseFromMemory (s_arrChunkEnd, s_arrChunkEnd.Length);
-                               }
-                               _WorkerRequest.CloseConnection ();
-                               _bClientDisconnected = true;
-                               closed = true;
-                       }
+                       if (closed)
+                               return;
+                       if (WorkerRequest != null)
+                               WorkerRequest.CloseConnection ();
+                       closed = true;
                }
 
-               internal void Dispose ()
+               public void End ()
                {
-                       if (_Writer != null) {
-                               _Writer.Dispose ();
-                               _Writer = null;
+                       if (context.TimeoutPossible) {
+                               Thread.CurrentThread.Abort (FlagEnd);
+                       } else {
+                               // If this is called from an async event, signal the completion
+                               // but don't throw.
+                               context.ApplicationInstance.CompleteRequest ();
                        }
                }
 
-               [MonoTODO("Handle callbacks into before done with session, needs to have a non ended flush here")]
-               internal void FlushAtEndOfRequest () 
+               // Generate:
+               //   Content-Length
+               //   Content-Type
+               //   Transfer-Encoding (chunked)
+               //   Cache-Control
+               void AddHeadersNoCache (ArrayList write_headers, bool final_flush)
                {
-                       Flush (true);
-               }
+                       //
+                       // Transfer-Encoding
+                       //
+                       if (use_chunked)
+                               write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", "chunked"));
+                       else if (transfer_encoding != null)
+                               write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", transfer_encoding));
 
-               public void End ()
-               {
-                       if (_bEnded)
-                               return;
+                       UnknownResponseHeader date_header = new UnknownResponseHeader ("Date",
+                                       DateTime.UtcNow.ToString ("r", CultureInfo.InvariantCulture));
+                       write_headers.Add (date_header);
 
-                       if (_Context.TimeoutPossible)
-#if !TARGET_J2EE
-                               Thread.CurrentThread.Abort (new StepCompleteRequest ());
-#else
-                               throw new vmw.@internal.j2ee.StopExecutionException();
+                       if (IsCached)
+                               cached_response.DateHeader = date_header;
+                                       
+                       if (redirect_location != null)
+                               write_headers.Add (new UnknownResponseHeader ("Location", redirect_location));
+                       
+                       //
+                       // If Content-Length is set.
+                       //
+                       if (content_length >= 0){
+                               write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderContentLength,
+                                                                     content_length.ToString (CultureInfo.InvariantCulture)));
+                       } else if (Buffer){
+                               if (final_flush){
+                                       //
+                                       // If we are buffering and this is the last flush, not a middle-flush,
+                                       // we know the content-length.
+                                       //
+                                       content_length = output_stream.total;
+                                       write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderContentLength,
+                                                                             content_length.ToString (CultureInfo.InvariantCulture)));
+                               } else {
+                                       //
+                                       // We are buffering, and this is a flush in the middle.
+                                       // If we are not chunked, we need to set "Connection: close".
+                                       //
+                                       if (use_chunked){
+#if DEBUG
+                                               Console.WriteLine ("Setting to close2");
 #endif
+                                               write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
+                                       }
+                               }
+                       } else {
+                               //
+                               // If the content-length is not set, and we are not buffering, we must
+                               // close at the end.
+                               //
+                               if (use_chunked){
+#if DEBUG
+                                       Console.WriteLine ("Setting to close");
+#endif
+                                       write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
+                               }
+                       }
 
-                       Flush ();
-                       _bEnded = true;
-                       _Context.ApplicationInstance.CompleteRequest ();
-               }
-
-               public void Flush ()
-               {
-                       if (closed)
-                               throw new HttpException ("Response already finished.");
+                       //
+                       // Cache Control, the cache policy takes precedence over the cache_control property.
+                       //
+                       if (cache_policy != null)
+                               cache_policy.SetHeaders (this, headers);
+                       else
+                               write_headers.Add (new UnknownResponseHeader ("Cache-Control", CacheControl));
+                       
+                       //
+                       // Content-Type
+                       //
+                       if (content_type != null){
+                               string header = content_type;
+
+                               if (charset_set || header == "text/plain" || header == "text/html") {
+                                       if (header.IndexOf ("charset=") == -1) {
+                                               if (charset == null || charset == "")
+                                                       charset = ContentEncoding.HeaderName;
+                                               header += "; charset=" + charset;
+                                       }
+                               }
+                               
+                               write_headers.Add (new UnknownResponseHeader ("Content-Type", header));
+                       }
 
-                       Flush (false);
+                       if (cookies != null && cookies.Count != 0){
+                               int n = cookies.Count;
+                               for (int i = 0; i < n; i++)
+                                       write_headers.Add (cookies.Get (i).GetCookieHeader ());
+                       }
+                       
                }
 
-               private void Flush (bool bFinish)
+               internal void WriteHeaders (bool final_flush)
                {
-                       if (_bFlushing || closed)
+                       if (headers_sent)
                                return;
 
-                       _bFlushing = true;
+                       if (WorkerRequest != null)
+                               WorkerRequest.SendStatus (status_code, StatusDescription);
 
-                       if (_Writer == null) {
-                               _TextWriter.Flush ();
-                               _bFlushing = false;
-                               return;
-                       }
-
-                       try {
-                               if (_bClientDisconnected)
-                                       return;
-
-                               long length = _Writer.BufferSize;
-                               if (!_bHeadersSent && !_bSuppressHeaders) {
-                                       if (bFinish) {
-                                               if (length == 0 && _lContentLength == 0)
-                                                       _sContentType = null;
-
-                                               SendHeaders ();
-                                               length = _Writer.BufferSize;
-                                               _WorkerRequest.SendCalculatedContentLength ((int) length);
-                                       } else {
-                                               if (_lContentLength == 0 && _iStatusCode == 200 &&
-                                                  _sTransferEncoding == null) {
-                                                       // Check we are going todo chunked encoding
-                                                       string sProto = Request.ServerVariables ["SERVER_PROTOCOL"];
-                                                       if (sProto != null && sProto == "HTTP/1.1") {
-                                                               AppendHeader (
-                                                                       HttpWorkerRequest.HeaderTransferEncoding,
-                                                                       "chunked");
-                                                       }  else {
-                                                               // Just in case, the old browsers send a HTTP/1.0
-                                                               // request with Connection: Keep-Alive
-                                                               AppendHeader (
-                                                                       HttpWorkerRequest.HeaderConnection,
-                                                                       "Close");
-                                                       }
-                                               }
-
-                                               length = _Writer.BufferSize;
-                                               SendHeaders ();
-                                       }
-                               }
-
-                               if (!filtered) {
-                                       _Writer.FilterData (false);
-                                       length = _Writer.BufferSize;
-                               }
-
-                               if (length == 0) {
-                                       if (bFinish && _bChunked) {
-                                               last_chunk_sent = true;
-                                               _WorkerRequest.SendResponseFromMemory (s_arrChunkEnd, s_arrChunkEnd.Length);
-                                       }
+                       if (cached_response != null)
+                               cached_response.SetHeaders (headers);
 
-                                       _WorkerRequest.FlushResponse (bFinish);
-                                       if (!bFinish)
-                                               _Writer.Clear ();
-                                       return;
+                       // If this page is cached use the cached headers
+                       // instead of the standard headers      
+                       ArrayList write_headers = headers;
+                       if (cached_headers != null)
+                               write_headers = cached_headers;
+                       else
+                               AddHeadersNoCache (write_headers, final_flush);
+
+                       //
+                       // Flush
+                       //
+                       if (context != null) {
+                               HttpApplication app_instance = context.ApplicationInstance;
+                               if (app_instance != null)
+                                       app_instance.TriggerPreSendRequestHeaders ();
+                       }
+                       if (WorkerRequest != null) {
+                               foreach (BaseResponseHeader header in write_headers){
+                                       header.SendContent (WorkerRequest);
                                }
+                       }
+                       headers_sent = true;
+               }
 
-                               if (!_bSuppressContent && Request.HttpMethod == "HEAD")
-                                       _bSuppressContent = true;
+               internal void DoFilter (bool close)
+               {
+                       if (output_stream.HaveFilter && context != null && context.Error == null)
+                               output_stream.ApplyFilter (close);
+               }
 
-                               if (_bSuppressContent)
-                                       _Writer.Clear ();
+               internal void Flush (bool final_flush)
+               {
+                       DoFilter (final_flush);
+                       if (!headers_sent){
+                               if (final_flush || status_code != 200)
+                                       use_chunked = false;
+                       }
 
-                               if (!_bSuppressContent) {
-                                       _bClientDisconnected = false;
-                                       if (_bChunked) {
-                                               Encoding oASCII = Encoding.ASCII;
+                       bool head = ((context != null) && (context.Request.HttpMethod == "HEAD"));
+                       if (suppress_content || head) {
+                               if (!headers_sent)
+                                       WriteHeaders (true);
+                               output_stream.Clear ();
+                               if (WorkerRequest != null)
+                                       output_stream.Flush (WorkerRequest, true); // ignore final_flush here.
+                               return;
+                       }
 
-                                               string chunk = Convert.ToString(_Writer.BufferSize, 16);
-                                               byte [] arrPrefix = oASCII.GetBytes (chunk + s_sChunkedPrefix);
+                       if (!headers_sent)
+                               WriteHeaders (final_flush);
 
-                                               _WorkerRequest.SendResponseFromMemory (arrPrefix,
-                                                                                      arrPrefix.Length);
+                       if (context != null) {
+                               HttpApplication app_instance = context.ApplicationInstance;
+                               if (app_instance != null)
+                                       app_instance.TriggerPreSendRequestContent ();
+                       }
 
-                                               _Writer.SendContent (_WorkerRequest);
+                       if (IsCached) {
+                               MemoryStream ms = output_stream.GetData ();
+                               cached_response.ContentLength = (int) ms.Length;
+                               cached_response.SetData (ms.GetBuffer ());
+                       }
 
-                                               _WorkerRequest.SendResponseFromMemory (s_arrChunkSuffix,
-                                                                                      s_arrChunkSuffix.Length);
-                                               if (bFinish) {
-                                                       last_chunk_sent = true;
-                                                       _WorkerRequest.SendResponseFromMemory (
-                                                                       s_arrChunkEnd, s_arrChunkEnd.Length);
-                                               }
-                                       } else {
-                                               _Writer.SendContent (_WorkerRequest);
-                                       }
-                               }
+                       if (WorkerRequest != null)
+                               output_stream.Flush (WorkerRequest, final_flush);
+               }
 
-                               _WorkerRequest.FlushResponse (bFinish);
-                               if (IsCached) {
-                                       cached_response.ContentLength = (int) length;
-                                       cached_response.SetData (_Writer.GetBuffer ());
-                               }
-                               _Writer.Clear ();
-                       } finally {
-                               if (bFinish)
-                                       closed = true;
-                               _bFlushing = false;
-                       }
+               public void Flush ()
+               {
+                       Flush (false);
                }
 
                public void Pics (string value)
@@ -889,7 +730,6 @@ namespace System.Web
                        AppendHeader ("PICS-Label", value);
                }
 
-
                public void Redirect (string url)
                {
                        Redirect (url, true);
@@ -897,184 +737,265 @@ namespace System.Web
 
                public void Redirect (string url, bool endResponse)
                {
-                       if (_bHeadersSent)
-                               throw new HttpException ("Headers has been sent to the client");
+                       if (headers_sent)
+                               throw new HttpException ("header have been already sent");
 
-                       Clear ();
-
-                       url = ApplyAppPathModifier (url);
+                       ClearHeaders ();
+                       ClearContent ();
+                       
                        StatusCode = 302;
-                       AppendHeader (HttpWorkerRequest.HeaderLocation, url);
+                       url = ApplyAppPathModifier (url);
+                       headers.Add (new UnknownResponseHeader ("Location", url));
 
                        // Text for browsers that can't handle location header
                        Write ("<html><head><title>Object moved</title></head><body>\r\n");
                        Write ("<h2>Object moved to <a href='" + url + "'>here</a></h2>\r\n");
                        Write ("</body><html>\r\n");
-
+                       
                        if (endResponse)
                                End ();
                }
 
-               internal bool RedirectCustomError (string errorPage)
+               public static void RemoveOutputCacheItem (string path)
                {
-                       if (_bHeadersSent)
-                               return false;
+                       if (path == null)
+                               throw new ArgumentNullException ("path");
 
-                       if (Request.QueryString ["aspxerrorpath"] != null)
-                               return false; // Prevent endless loop
+                       if (path.Length == 0)
+                               return;
 
-                       Redirect (errorPage + "?aspxerrorpath=" + Request.Path, false);
-                       return true;
+                       if (path [0] != '/')
+                               throw new ArgumentException ("'" + path + "' is not an absolute virtual path.");
+
+                       HttpRuntime.Cache.Remove (path);
                }
-               
+
+               public void SetCookie (HttpCookie cookie)
+               {
+                       AppendCookie (cookie);
+               }
+
                public void Write (char ch)
                {
-                       _TextWriter.Write(ch);
+                       Output.Write (ch);
                }
 
                public void Write (object obj)
                {
-                       _TextWriter.Write(obj);
+                       if (obj == null)
+                               return;
+                       
+                       Output.Write (obj.ToString ());
                }
-
-               public void Write (string str)
+               
+               public void Write (string s)
                {
-                       _TextWriter.Write (str);
+                       Output.Write (s);
                }
-
+               
                public void Write (char [] buffer, int index, int count)
                {
-                       _TextWriter.Write (buffer, index, count);
+                       Output.Write (buffer, index, count);
                }
 
-               public static void RemoveOutputCacheItem (string path)
+               internal void WriteFile (FileStream fs, long offset, long size)
                {
-                       if (path == null)
-                               throw new ArgumentNullException ("path");
-                       
-                       if (!UrlUtils.IsRooted (path))
-                               throw new ArgumentException ("Invalid path for HttpResponse.RemoveOutputCacheItem '" +
-                                       path + "'. An absolute virtual path is expected.");
+                       byte [] buffer = new byte [32*1024];
 
-                       Cache cache = HttpRuntime.Cache;
-                       cache.Remove (path);
+                       if (offset != 0)
+                               fs.Position = offset;
+
+                       long remain = size;
+                       int n;
+                       while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
+                               remain -= n;
+                               output_stream.Write (buffer, 0, n);
+                       }
+               }
+               
+               public void WriteFile (string filename)
+               {
+                       WriteFile (filename, false);
                }
 
-               public void SetCookie (HttpCookie cookie)
+               public void WriteFile (string filename, bool readIntoMemory)
                {
-                       if (_bHeadersSent)
-                               throw new HttpException ("Cannot append cookies after HTTP headers have been sent");
+                       if (filename == null)
+                               throw new ArgumentNullException ("filename");
 
-                       Cookies.Add (cookie);
+                       if (readIntoMemory){
+                               using (FileStream fs = File.OpenRead (filename))
+                                       WriteFile (fs, 0, fs.Length);
+                       } else {
+                               FileInfo fi = new FileInfo (filename);
+                               output_stream.WriteFile (filename, 0, fi.Length);
+                       }
+                       if (buffer)
+                               return;
+
+                       output_stream.ApplyFilter (false);
+                       Flush ();
                }
 
-#if NET_1_1
-               // LAMESPEC: added in a service pack for 1.1 and available in 2.0 beta
-               [MonoTODO]
-               public void TransmitFile (string filename)
+#if TARGET_JVM
+               public void WriteFile (IntPtr fileHandle, long offset, long size) {
+                       throw new NotSupportedException("IntPtr not supported");
+               }
+#else
+               public void WriteFile (IntPtr fileHandle, long offset, long size)
                {
-                       throw new NotImplementedException ();
+                       if (offset < 0)
+                               throw new ArgumentNullException ("offset can not be negative");
+                       if (size < 0)
+                               throw new ArgumentNullException ("size can not be negative");
+
+                       if (size == 0)
+                               return;
+
+                       // Note: this .ctor will throw a SecurityException if the caller 
+                       // doesn't have the UnmanagedCode permission
+                       using (FileStream fs = new FileStream (fileHandle, FileAccess.Read))
+                               WriteFile (fs, offset, size);
+
+                       if (buffer)
+                               return;
+                       output_stream.ApplyFilter (false);
+                       Flush ();
                }
 #endif
 
-               private void WriteFromStream (Stream stream, long offset, long length, long bufsize)
+               public void WriteFile (string filename, long offset, long size)
                {
-                       if (offset < 0 || length <= 0)
+                       if (filename == null)
+                               throw new ArgumentNullException ("filename");
+                       if (offset < 0)
+                               throw new ArgumentNullException ("offset can not be negative");
+                       if (size < 0)
+                               throw new ArgumentNullException ("size can not be negative");
+
+                       if (size == 0)
                                return;
                        
-                       long stLength = stream.Length;
-                       if (offset + length > stLength)
-                               length = stLength - offset;
-
-                       if (offset > 0)
-                               stream.Seek (offset, SeekOrigin.Begin);
-
-                       byte [] fileContent = new byte [bufsize];
-                       int count = (int) Math.Min (Int32.MaxValue, bufsize);
-                       while (length > 0 && (count = stream.Read (fileContent, 0, count)) != 0) {
-                               _Writer.WriteBytes (fileContent, 0, count);
-                               length -= count;
-                               count = (int) Math.Min (length, fileContent.Length);
-                       }
-               }
+                       FileStream fs = File.OpenRead (filename);
+                       WriteFile (fs, offset, size);
 
-               public void WriteFile (string filename)
-               {
-                       WriteFile (filename, false);
-               }
+                       if (buffer)
+                               return;
 
-               public void WriteFile (string filename, bool readIntoMemory)
+                       output_stream.ApplyFilter (false);
+                       Flush ();
+               }
+#if NET_2_0
+               [MonoTODO]
+               public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
                {
-#if TARGET_J2EE
-                       if ((this.Request != null) && ((filename.Length <= 2)
-                                       || ((filename[0] != '\\') && (filename[1] != ':'))))
-                               filename = Request.MapPath(filename);
+                       throw new NotImplementedException ();
+               }
 #endif
-                       FileStream fs = null;
-                       try {
-                               fs = File.OpenRead (filename);
-                               long size = fs.Length;
-                               if (readIntoMemory) {
-                                       WriteFromStream (fs, 0, size, size);
-                               } else {
-                                       WriteFromStream (fs, 0, size, 30702);
-                               }
-                       } finally {
-                               if (fs != null)
-                                       fs.Close ();
-                       }
+               //
+               // Like WriteFile, but never buffers, so we manually Flush here
+               //
+               public void TransmitFile (string filename) 
+               {
+                       if (filename == null)
+                               throw new ArgumentNullException ("filename");
+
+                       TransmitFile (filename, false);
                }
 
-               public void WriteFile (string filename, long offset, long size)
+               internal void TransmitFile (string filename, bool final_flush)
                {
-                       FileStream fs = null;
-#if TARGET_J2EE
-                       if ((this.Request != null) && ((filename.Length <= 2)
-                               || ((filename[0] != '\\') && (filename[1] != ':'))))
-                               filename = Request.MapPath(filename);
-#endif
-                       try {
-                               fs = File.OpenRead (filename);
-                               WriteFromStream (fs, offset, size, 30702);
-                       } finally {
-                               if (fs != null)
-                                       fs.Close ();
-                       }
+                       FileInfo fi = new FileInfo (filename);
+                       using (Stream s = fi.OpenRead ()); // Just check if we can read.
+                       output_stream.WriteFile (filename, 0, fi.Length);
+                       output_stream.ApplyFilter (final_flush);
+                       Flush (final_flush);
                }
+               
 
-               public void WriteFile (IntPtr fileHandle, long offset, long size)
+#region Session state support
+               internal void SetAppPathModifier (string app_modifier)
+               {
+                       app_path_mod = app_modifier;
+               }
+#endregion
+               
+#region Cache Support
+               internal void SetCachedHeaders (ArrayList headers)
                {
-                       FileStream fs = null;
-                       try {
-                               fs = new FileStream (fileHandle, FileAccess.Read);
-                               WriteFromStream (fs, offset, size, 30702);
-                       } finally {
-                               if (fs != null)
-                                       fs.Close ();
+                       cached_headers = headers;
+               }
+
+               internal bool IsCached {
+                       get {
+                               return cached_response != null;
                        }
-               }   
+               }
 
-               [MonoTODO()]
-               internal void OnCookieAdd (HttpCookie cookie)
+               public HttpCachePolicy Cache {
+                       get {
+                               if (cache_policy == null) {
+                                       cache_policy = new HttpCachePolicy ();
+                                       cache_policy.CacheabilityUpdated += new CacheabilityUpdatedCallback (OnCacheabilityUpdated);
+                               }
+                               
+                               return cache_policy;
+                       }
+               }
+               
+               private void OnCacheabilityUpdated (object sender, CacheabilityUpdatedEventArgs e)
                {
-#if TARGET_J2EE        //naive implementation
-                       Request.Cookies.Add(cookie);
-#endif
+                       if (e.Cacheability >= HttpCacheability.Server && !IsCached)
+                               cached_response = new CachedRawResponse (cache_policy);
+                       else if (e.Cacheability <= HttpCacheability.Private)
+                               cached_response = null;
                }
 
-               [MonoTODO("Do we need this?")]
-               internal void OnCookieChange (HttpCookie cookie)
+               internal CachedRawResponse GetCachedResponse ()
                {
+                       cached_response.StatusCode = StatusCode;
+                       cached_response.StatusDescription = StatusDescription;
+                       return cached_response;
                }
 
-               [MonoTODO()]
-               internal void GoingToChangeCookieColl ()
+               //
+               // This is one of the old ASP compatibility methods, the real cache
+               // control is in the Cache property, and this is a second class citizen
+               //
+               public string CacheControl {
+                       set {
+                               if (value == null || value == "") {
+                                       Cache.SetCacheability (HttpCacheability.NoCache);
+                                       user_cache_control = null;
+                               } else if (String.Compare (value, "public", true, CultureInfo.InvariantCulture) == 0) {
+                                       Cache.SetCacheability (HttpCacheability.Public);
+                                       user_cache_control = "public";
+                               } else if (String.Compare (value, "private", true, CultureInfo.InvariantCulture) == 0) {
+                                       Cache.SetCacheability (HttpCacheability.Private);
+                                       user_cache_control = "private";
+                               } else if (String.Compare (value, "no-cache", true, CultureInfo.InvariantCulture) == 0) {
+                                       Cache.SetCacheability (HttpCacheability.NoCache);
+                                       user_cache_control = "no-cache";
+                               } else
+                                       throw new ArgumentException ("CacheControl property only allows `public', " +
+                                                                    "`private' or no-cache, for different uses, use " +
+                                                                    "Response.AppendHeader");
+                       }
+
+                       get { return (user_cache_control != null) ? user_cache_control : "private"; }
+               }
+#endregion
+
+               internal int GetOutputByteCount ()
                {
+                       return output_stream.GetTotalLength ();
                }
 
-               [MonoTODO()]
-               internal void ChangedCookieColl ()
+               internal void ReleaseResources ()
                {
+                       output_stream.ReleaseResources (true);
+                       output_stream = null;
                }
        }
 }
+