New test.
[mono.git] / mcs / class / System.Web / System.Web / HttpResponse.cs
index 0236dd31b4d2de594a7d745662df2bff9acdb85f..890078fb4a179bffa4567190965a291a6c06bc51 100644 (file)
-// 
-// System.Web.HttpResponse
 //
-// Authors:
-//     Patrik Torstensson (Patrik.Torstensson@labs2.com)
+// System.Web.HttpResponse.cs 
+//
+// 
+// Author:
+//     Miguel de Icaza (miguel@novell.com)
 //     Gonzalo Paniagua Javier (gonzalo@ximian.com)
 //
-// (c) 2002 Ximian, Inc. (http://www.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
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// 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 = { 10, 13 };
-               static byte [] s_arrChunkEnd = { 10 , 13 };
-               static string s_sChunkedPrefix = "\r\n";
+using System.Globalization;
+using System.Security.Permissions;
 
-               ArrayList _Headers;
-                       
-               bool _bClientDisconnected;
-               bool _bSuppressHeaders;
-               bool _bSuppressContent;
-               bool _bChunked;
-               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 _ClientDisconnected;
-               bool closed;
-
-               string  _sContentType;
-               string  _sCacheControl;
-               string  _sTransferEncoding;
-               string  _sCharset;
-               string  _sStatusDescription;
-
-               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;
-                
-               public HttpResponse (TextWriter output)
-               {
-                        _bBuffering = true;
-                        _bFlushing = false;
-                        _bHeadersSent = false;
-
-                        _Headers = new ArrayList ();
 
-                        _sContentType = "text/html";
-
-                        _iStatusCode = 200;
-                        _sCharset = null;
-                        _sCacheControl = null;
-
-                        _lContentLength = 0;
-                        _bSuppressContent = false;
-                        _bSuppressHeaders = false;
-                        _bClientDisconnected = false;
-
-                        _bChunked = false;
+               //
+               // Transfer encoding state
+               //
+               string transfer_encoding;
+               internal bool use_chunked;
+               
+               bool closed;
+               internal bool suppress_content;
 
-                        _TextWriter = output;
-               }
+               //
+               // Session State
+               //
+               string app_path_mod;
+               
+               //
+               // Passed as flags
+               //
+               internal object FlagEnd = new object ();
 
-               internal HttpResponse (HttpWorkerRequest WorkerRequest, HttpContext Context)
+               internal HttpResponse ()
                {
-                        _Context = Context;
-                        _WorkerRequest = WorkerRequest;
-
-                        _bBuffering = true;
-                        _bFlushing = false;
-                        _bHeadersSent = false;
-
-                        _Headers = new ArrayList ();
-
-                        _sContentType = "text/html";
-
-                        _iStatusCode = 200;
-                        _sCharset = null;
-                        _sCacheControl = null;
-
-                        _lContentLength = 0;
-                        _bSuppressContent = false;
-                        _bSuppressHeaders = false;
-                        _bClientDisconnected = false;
-
-                        _bChunked = false;
+                       output_stream = new HttpResponseStream (this);
                }
 
-               internal void InitializeWriter ()
+               public HttpResponse (TextWriter writer) : this ()
                {
-                       // 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 ()
-               {
-                       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; }
+                       if (worker_request != null)
+                               use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1");
                }
                
-               internal void CacheResponse (HttpRequest request) {
-                       cached_response = new CachedRawResponse (_CachePolicy);
-               }
-
-               internal CachedRawResponse GetCachedResponse () {
-                       cached_response.StatusCode = StatusCode;
-                       cached_response.StatusDescription = StatusDescription;
-                       return cached_response;
-               }
-
-               internal void SetCachedHeaders (ArrayList headers)
+               internal TextWriter SetTextWriter (TextWriter writer)
                {
-                       cached_headers = headers;
+                       TextWriter prev = writer;
+                       
+                       this.writer = writer;
+                       
+                       return prev;
                }
                
-               [MonoTODO("We need to add cache headers also")]
-               private ArrayList GenerateHeaders ()
-               {
-                       ArrayList oHeaders = new ArrayList (_Headers.ToArray ());
-
-                       oHeaders.Add (new HttpResponseHeader ("X-Powered-By", "Mono"));
-                       // save culture info, we need us info here
-                       CultureInfo oSavedInfo = Thread.CurrentThread.CurrentCulture;
-                       Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
-
-                       string date = DateTime.Now.ToUniversalTime ().ToString ("ddd, d MMM yyyy HH:mm:ss ");
-                       oHeaders.Add (new HttpResponseHeader ("Date", date + "GMT"));
-
-                       Thread.CurrentThread.CurrentCulture = oSavedInfo;
-
-                       if (_lContentLength > 0) {
-                               oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderContentLength,
-                                                                     _lContentLength.ToString ()));
-                       }
-
-                       if (_sContentType != null) {
-                               if (_sContentType.IndexOf ("charset=") == -1) {
-                                       if (Charset.Length == 0) {
-                                               Charset = ContentEncoding.HeaderName;
-                                       }
-
-                                       // Time to build our string
-                                       if (Charset.Length > 0) {
-                                               _sContentType += "; charset=" + Charset;
-                                       }
-                               }
-
-                               oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderContentType,
-                                                                     _sContentType));
-                       }
-
-                       if (_CachePolicy != null)
-                               _CachePolicy.SetHeaders (this, oHeaders);
-                       
-                       if (_sCacheControl != null) {
-                               oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderPragma,
-                                                                     _sCacheControl));
-                       }
-
-                       if (_sTransferEncoding != null) {
-                               oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderTransferEncoding,
-                                                                     _sTransferEncoding));
+               public bool Buffer {
+                       get {
+                               return buffer;
                        }
 
-                       if (_Cookies != null) {
-                               int length = _Cookies.Count;
-                               for (int i = 0; i < length; i++) {
-                                       oHeaders.Add (_Cookies.Get (i).GetCookieHeader ());
-                               }
+                       set {
+                               buffer = value;
                        }
-#if NET_1_1
-                       if (redirectLocation != null)
-                               oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderLocation,
-                                                                     redirectLocation));
-#endif
-                       return oHeaders;
                }
-               
-               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);
-                       
-                       _bHeadersSent = true;
-               }
-
-               public string Status
-               {
+               public bool BufferOutput {
                        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;
+               //
+               // 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;
                        }
 
-                       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 ();
-               }
-
-               public string ApplyAppPathModifier (string virtualPath)
-               {
-                       if (virtualPath == null)
-                               return null;
-
-                       if (virtualPath == "")
-                               return _Context.Request.RootVirtualDir;
+                       set {
+                               if (value == null)
+                                       throw new ArgumentException ("ContentEncoding can not be null");
 
-                       if (UrlUtils.IsRelativeUrl (virtualPath)) {
-                               virtualPath = UrlUtils.Combine (_Context.Request.RootVirtualDir, virtualPath);
-                       } else if (UrlUtils.IsRooted (virtualPath)) {
-                               virtualPath = UrlUtils.Reduce (virtualPath);
+                               encoding = value;
+                               HttpWriter http_writer = writer as HttpWriter;
+                               if (http_writer != null)
+                                       http_writer.SetEncoding (encoding);
                        }
-
-                       if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) > 0)
-                               virtualPath = UrlUtils.Combine (app_path_mod, virtualPath);
-
-                       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 ();
-
-                               return _CachePolicy;
+                               if (cookies == null)
+                                       cookies = new HttpCookieCollection (true, false);
+                               return cookies;
                        }
                }
-
-               [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");
-
-                               _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 output_stream;
+                       }
+               }
+               
+               public string RedirectLocation {
                        get {
-                               return _expiresInMinutes;
+                               return redirect_location;
                        }
 
                        set {
-                               if (!_expiresInMinutesSet || (value < _expiresInMinutes))\r
-                               {\r
-                                       _expiresInMinutes = value;\r
-                                       Cache.SetExpires(_Context.Timestamp.Add(new TimeSpan(0, _expiresInMinutes, 0)));\r
-                               }
-                               _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)\r
-                               {\r
-                                       _expiresAbsolute = value;\r
-                                       Cache.SetExpires(_expiresAbsolute); \r
+                               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 (_ClientDisconnected)
-                                       return false;
+                               if (status_description == null)
+                                       status_description = HttpWorkerRequest.GetStatusDescription (status_code);
 
-                               if (null != _WorkerRequest && (!_WorkerRequest.IsClientConnected ())) {
-                                       _ClientDisconnected = false;
-                                       return false;
-                               }
+                               return status_description;
+                       }
 
-                               return true;
+                       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 {
-                               if (_bHeadersSent)
-                                       throw new HttpException ("Headers has been sent to the client");
-
-                               _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;
-                               if (value.Equals ("chunked")) {
-                                       _bChunked = true;
-                               } else {
-                                       _bChunked = false;
-                               }
-                               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 ()) {
-                       case "content-length":
-                               _lContentLength = Int64.Parse (value);
-                               break;
-                       case "content-type":
-                               _sContentType = value;
-                               break;
-                       case "transfer-encoding":
-                               _sTransferEncoding = value;
-                               if (value.Equals ("chunked")) {
-                                       _bChunked = true;
-                               } else {
-                                       _bChunked = false;
-                               }
-                               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 ();
                }
 
                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";
-
-                       _iStatusCode = 200;
-                       _sCharset = null;
-                       _Headers = new ArrayList ();
-                       _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) {
-                               _bClientDisconnected = false;
-                               _WorkerRequest.CloseConnection ();
-                               _bClientDisconnected = 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 ();
+                       }
+               }
+
+               // Generate:
+               //   Content-Length
+               //   Content-Type
+               //   Transfer-Encoding (chunked)
+               //   Cache-Control
+               void AddHeadersNoCache (ArrayList write_headers, bool final_flush)
+               {
+                       //
+                       // 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));
+
+                       UnknownResponseHeader date_header = new UnknownResponseHeader ("Date",
+                                       DateTime.UtcNow.ToString ("r", CultureInfo.InvariantCulture));
+                       write_headers.Add (date_header);
+
+                       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"));
+                               }
                        }
-               }
 
-               [MonoTODO("Handle callbacks into before done with session, needs to have a non ended flush here")]
-               internal void FlushAtEndOfRequest () 
-               {
-                       Flush (true);
+                       //
+                       // 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));
+                       }
+
+                       if (cookies != null && cookies.Count != 0){
+                               int n = cookies.Count;
+                               for (int i = 0; i < n; i++)
+                                       write_headers.Add (cookies.Get (i).GetCookieHeader ());
+                       }
+                       
                }
 
-               public void End ()
+               internal void WriteHeaders (bool final_flush)
                {
-                       if (_bEnded)
+                       if (headers_sent)
                                return;
 
-                       if (_Context.TimeoutPossible)
-                               Thread.CurrentThread.Abort (new StepCompleteRequest ());
+                       if (WorkerRequest != null)
+                               WorkerRequest.SendStatus (status_code, StatusDescription);
 
-                       Flush ();
-                       _bEnded = true;
-                       _Context.ApplicationInstance.CompleteRequest ();
+                       if (cached_response != null)
+                               cached_response.SetHeaders (headers);
+
+                       // 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;
                }
 
-               public void Flush ()
+               internal void DoFilter (bool close)
                {
-                       if (closed)
-                               throw new HttpException ("Response already finished.");
-
-                       Flush (false);
+                       if (output_stream.HaveFilter && context != null && context.Error == null)
+                               output_stream.ApplyFilter (close);
                }
 
-               private void Flush (bool bFinish)
+               internal void Flush (bool final_flush)
                {
-                       if (_bFlushing || closed)
-                               return;
-
-                       _bFlushing = true;
+                       DoFilter (final_flush);
+                       if (!headers_sent){
+                               if (final_flush || status_code != 200)
+                                       use_chunked = false;
+                       }
 
-                       if (_Writer == null) {
-                               _TextWriter.Flush ();
-                               _bFlushing = false;
+                       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;
                        }
 
-                       try {
-                               if (_bClientDisconnected)
-                                       return;
-
-                               long length = _Writer.BufferSize;
-                               if (!_bHeadersSent && !_bSuppressHeaders) {
-                                       if (bFinish) {
-                                               if (length == 0 && _lContentLength == 0)
-                                                       _sContentType = null;
-
-                                               SendHeaders ();
-                                               length = _Writer.BufferSize;
-                                               if (length != 0)
-                                                       _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"];
-                                                       sProto = "HTTP/1.0"; // Remove this line when we support properly
-                                                                            // chunked content
-
-                                                       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) {
-                                       _WorkerRequest.FlushResponse (bFinish);
-                                       if (!bFinish)
-                                               _Writer.Clear ();
-                                       return;
-                               }
-
-                               if (!_bSuppressContent && Request.HttpMethod == "HEAD")
-                                       _bSuppressContent = true;
-
-                               if (!_bSuppressContent) {
-                                       _bClientDisconnected = false;
-                                       if (_bChunked) {
-                                               Encoding oASCII = Encoding.ASCII;
+                       if (!headers_sent)
+                               WriteHeaders (final_flush);
 
-                                               string chunk = Convert.ToString(_Writer.BufferSize, 16);
-                                               byte [] arrPrefix = oASCII.GetBytes (chunk + s_sChunkedPrefix);
-
-                                               _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)
-                                                       _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)
@@ -862,7 +730,6 @@ namespace System.Web
                        AppendHeader ("PICS-Label", value);
                }
 
-
                public void Redirect (string url)
                {
                        Redirect (url, true);
@@ -870,162 +737,265 @@ namespace System.Web
 
                public void Redirect (string url, bool endResponse)
                {
-                       if (_bHeadersSent)
-                               throw new HttpException ("Headers has been sent to the client");
-
-                       Clear ();
+                       if (headers_sent)
+                               throw new HttpException ("header have been already sent");
 
-                       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];
+
+                       if (offset != 0)
+                               fs.Position = offset;
 
-                       Cache cache = HttpRuntime.Cache;
-                       cache.Remove (path);
+                       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 ();
                }
 
-               private void WriteFromStream (Stream stream, long offset, long length, long bufsize)
+#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)
                {
-                       if (offset < 0 || length <= 0)
+                       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);
-                       }
+
+                       // 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
 
-               public void WriteFile (string filename)
+               public void WriteFile (string filename, long offset, long size)
                {
-                       WriteFile (filename, false);
-               }
+                       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");
 
-               public void WriteFile (string filename, bool readIntoMemory)
+                       if (size == 0)
+                               return;
+                       
+                       FileStream fs = File.OpenRead (filename);
+                       WriteFile (fs, offset, size);
+
+                       if (buffer)
+                               return;
+
+                       output_stream.ApplyFilter (false);
+                       Flush ();
+               }
+#if NET_2_0
+               [MonoTODO]
+               public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
                {
-                       FileStream fs = null;
-                       try {
-                               fs = File.OpenRead (filename);
-                               long size = fs.Length;
-                               if (readIntoMemory) {
-                                       WriteFromStream (fs, 0, size, size);
-                               } else {
-                                       WriteFromStream (fs, 0, size, 8192);
-                               }
-                       } finally {
-                               if (fs != null)
-                                       fs.Close ();
-                       }
+                       throw new NotImplementedException ();
                }
+#endif
+               //
+               // Like WriteFile, but never buffers, so we manually Flush here
+               //
+               public void TransmitFile (string filename) 
+               {
+                       if (filename == null)
+                               throw new ArgumentNullException ("filename");
 
-               public void WriteFile (string filename, long offset, long size)
+                       TransmitFile (filename, false);
+               }
+
+               internal void TransmitFile (string filename, bool final_flush)
                {
-                       FileStream fs = null;
-                       try {
-                               fs = File.OpenRead (filename);
-                               WriteFromStream (fs, offset, size, 8192);
-                       } 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, 8192);
-                       } 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 (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;
                }
        }
 }
+