Call SetEndOfSendNotification
[mono.git] / mcs / class / System.Web / System.Web / HttpResponse.cs
index 015c3ecc501cbf2076dc24a279e0cd411c79e50e..9aa99d9244bba7f1fefdf3e5dd6a763be74ae5a0 100644 (file)
@@ -1,13 +1,13 @@
-// 
-// 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)
+//      Marek Habersack <mhabersack@novell.com>
 //
-
+// Copyright (C) 2005-2010 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;
+using System.Web.Configuration;
+using System.Globalization;
+using System.Security.Permissions;
+using System.Web.Hosting;
+using System.Web.SessionState;
+
+#if NET_4_0
+using System.Web.Routing;
+#endif
 
 namespace System.Web
-{
-       public sealed class HttpResponse
+{      
+       // CAS - no InheritanceDemand here as the class is sealed
+       [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
+       public sealed partial 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";
+               internal HttpWorkerRequest WorkerRequest;
+               internal HttpResponseStream output_stream;
+               internal bool buffer = true;
 
-               ArrayList _Headers;
-                       
-               bool _bClientDisconnected;
-               bool _bSuppressHeaders;
-               bool _bSuppressContent;
-               bool _bChunked;
-               bool _bEnded;
-               bool _bBuffering;
-               bool _bHeadersSent;
-               bool _bFlushing;
-               bool filtered;
-               long _lContentLength;
-               int _iStatusCode;
+               ArrayList fileDependencies;
                
-               int _expiresInMinutes;
-               bool _expiresInMinutesSet;
-               DateTime _expiresAbsolute;
-               bool _expiresAbsoluteSet;
+               HttpContext context;
+               TextWriter writer;
+               HttpCachePolicy cache_policy;
+               Encoding encoding;
+               HttpCookieCollection cookies;
+               
+               int status_code = 200;
+               string status_description = "OK";
 
-               bool _ClientDisconnected;
+               string content_type = "text/html";
+               string charset;
+               bool charset_set;
+               CachedRawResponse cached_response;
+               string user_cache_control = "private";
+               string redirect_location;
+               string version_header;
+               bool version_header_checked;
+               
+               //
+               // 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.
+               //
+
+               HttpHeaderCollection headers;
+               bool headers_sent;
+               NameValueCollection cached_headers;
+
+               //
+               // Transfer encoding state
+               //
+               string transfer_encoding;
+               internal bool use_chunked;
+               
                bool closed;
+               internal bool suppress_content;
 
-               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;
+               //
+               // Session State
+               //
+               string app_path_mod;
+               bool is_request_being_redirected;
+               Encoding headerEncoding;
 
-               ArrayList fileDependencies;
-               CachedRawResponse cached_response;
-               ArrayList cached_headers;
-#if NET_1_1
-               string redirectLocation;
-#endif
-
-               string app_path_mod = null;
-                
-               public HttpResponse (TextWriter output)
+               internal HttpResponse ()
                {
-                        _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;
-
-                        _TextWriter = output;
+                       output_stream = new HttpResponseStream (this);
+                       writer = new HttpWriter (this);
                }
 
-               internal HttpResponse (HttpWorkerRequest WorkerRequest, HttpContext Context)
+               public HttpResponse (TextWriter writer) : this ()
                {
-                        _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;
-               }
 
-               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 ()
-               {
-                       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);
-
-                       filtered = true;
-               }
+                       WorkerRequest = worker_request;
+                       this.context = context;
 
-               internal bool IsCached {
-                       get { return cached_response != null; }
-               }
-
-               internal CachedRawResponse GetCachedResponse () {
-                       cached_response.StatusCode = StatusCode;
-                       cached_response.StatusDescription = StatusDescription;
-                       return cached_response;
+#if !TARGET_J2EE
+                       if (worker_request != null)
+                               use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1");
+#endif
+                       writer = new HttpWriter (this);
                }
 
-               internal void SetCachedHeaders (ArrayList headers)
+               internal TextWriter SetTextWriter (TextWriter writer)
                {
-                       cached_headers = headers;
+                       TextWriter prev = this.writer;
+                       this.writer = writer;
+                       return prev;
                }
-               
-               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.UtcNow.ToString ("ddd, d MMM yyyy HH:mm:ss ");
-                       HttpResponseHeader date_header = new HttpResponseHeader ("Date", date + "GMT");
-                       oHeaders.Add (date_header);
-                       
-                       if (IsCached)
-                               cached_response.DateHeader = date_header;
-
-                       Thread.CurrentThread.CurrentCulture = oSavedInfo;
-
-                       if (_lContentLength > 0) {
-                               oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderContentLength,
-                                                                     _lContentLength.ToString ()));
-                       }
 
-                       // 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;
-                                       }
+               internal string VersionHeader {
+                       get {
+                               if (!version_header_checked && version_header == null) {
+                                       version_header_checked = true;
+                                       HttpRuntimeSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime") as HttpRuntimeSection;
+                                       if (config != null && config.EnableVersionHeader)
+                                               version_header = Environment.Version.ToString (3);
                                }
 
-                               oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderContentType, ctype));
+                               return version_header;
                        }
+               }
 
-                       if (_CachePolicy != null)
-                               _CachePolicy.SetHeaders (this, oHeaders);
+               internal HttpContext Context {
+                       get { return context; }
+                       set { context = value; }
+               }
                        
-                       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 ());
-                               }
+               internal string[] FileDependencies {
+                       get {
+                               if (fileDependencies == null || fileDependencies.Count == 0)
+                                       return new string[0] {};
+                               return (string[]) fileDependencies.ToArray (typeof (string));
                        }
-#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;
+               ArrayList FileDependenciesArray {
+                       get {
+                               if (fileDependencies == null)
+                                       fileDependencies = new ArrayList ();
+                               return fileDependencies;
+                       }
                }
-
-               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 (!virtualPath.StartsWith (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 {
-                               if (value == null)
-                                       throw new ArgumentNullException ("Can't set a null as encoding");
-
-                               _ContentEncoding = value;
-
-                               if (_Writer != null)
-                                       _Writer.Update ();
+                               output_stream.Filter = value;
                        }
                }
 
-               public string ContentType
-               {
+               public Encoding HeaderEncoding {
                        get {
-                               return _sContentType;
+                               if (headerEncoding == null) {
+                                       GlobalizationSection gs = WebConfigurationManager.SafeGetSection ("system.web/globalization", typeof (GlobalizationSection)) as GlobalizationSection;
+
+                                       if (gs == null)
+                                               headerEncoding = Encoding.UTF8;
+                                       else {
+                                               headerEncoding = gs.ResponseHeaderEncoding;
+                                               if (headerEncoding == Encoding.Unicode)
+                                                       throw new HttpException ("HeaderEncoding must not be Unicode");
+                                       }
+                               }
+                               return headerEncoding;
                        }
-
                        set {
-                               if (_bHeadersSent)
-                                       throw new HttpException ("Headers has been sent to the client");
-
-                               _sContentType = value;
+                               if (headers_sent)
+                                       throw new HttpException ("headers have already been sent");
+                               if (value == null)
+                                       throw new ArgumentNullException ("HeaderEncoding");
+                               if (value == Encoding.Unicode)
+                                       throw new HttpException ("HeaderEncoding must not be Unicode");
+                               headerEncoding = value;
                        }
                }
 
-               public HttpCookieCollection Cookies
-               {
+               public NameValueCollection Headers {
                        get {
-                               if (null == _Cookies)
-                                       _Cookies = new HttpCookieCollection (this, false);
+                               if (headers == null)
+                                       headers = new HttpHeaderCollection ();
 
-                               return _Cookies;
+                               return headers;
                        }
                }
 
-               public int Expires
-               {
+               
+               public bool IsClientConnected {
                        get {
-                               return _expiresInMinutes;
-                       }
+                               if (WorkerRequest == null)
+                                       return true; // yep that's true
 
-                       set {
-                               if (!_expiresInMinutesSet || (value < _expiresInMinutes))
-                               {
-                                       _expiresInMinutes = value;
-                                       Cache.SetExpires(_Context.Timestamp.Add(new TimeSpan(0, _expiresInMinutes, 0)));
-                               }
-                               _expiresInMinutesSet = true;
+                               return WorkerRequest.IsClientConnected ();
                        }
                }
 
-               public DateTime ExpiresAbsolute
-               {
+               public bool IsRequestBeingRedirected {
+                       get { return is_request_being_redirected; }
+               }
+
+               public TextWriter Output {
                        get {
-                               return _expiresAbsolute;
+                               return writer;
                        }
+#if NET_4_0
+                       set { writer = value; }
+#endif
+               }
 
-                       set {
-                               if (!_expiresAbsoluteSet || value.CompareTo(_expiresAbsolute)<0)
-                               {
-                                       _expiresAbsolute = value;
-                                       Cache.SetExpires(_expiresAbsolute); 
-                               }
-                               _expiresAbsoluteSet = true;
+               public Stream OutputStream {
+                       get {
+                               return output_stream;
                        }
                }
-
-               public Stream Filter
-               {
+               
+               public string RedirectLocation {
                        get {
-                               if (_Writer != null)
-                                       return _Writer.GetActiveFilter ();
-
-                               return null;
+                               return redirect_location;
                        }
 
                        set {
-                               if (_Writer == null)
-                                       throw new HttpException ("Filtering is not allowed");
-
-                               _Writer.ActivateFilter (value);
+                               redirect_location = value;
                        }
                }
+               
+               public string Status {
+                       get { return String.Concat (status_code.ToString (), " ", StatusDescription); }
 
-               public bool IsClientConnected
-               {
-                       get {
-                               if (_ClientDisconnected)
-                                       return false;
-
-                               if (null != _WorkerRequest && (!_WorkerRequest.IsClientConnected ())) {
-                                       _ClientDisconnected = false;
-                                       return false;
-                               }
-
-                               return true;
-                       }
-               }
-      
-               public TextWriter Output
-               {
-                       get {
-                               return _TextWriter;
+                       set {
+                               int p = value.IndexOf (' ');
+                               if (p == -1)
+                                       throw new HttpException ("Invalid format for the Status property");
+
+                               string s = value.Substring (0, p);
+                               
+                               if (!Int32.TryParse (s, out status_code))
+                                       throw new HttpException ("Invalid format for the Status property");
+                               
+                               status_description = value.Substring (p+1);
                        }
                }
 
-               public Stream OutputStream
-               {
-                       get {
-                               if (_Writer == null)
-                                       throw new HttpException ("an Output stream not available when " +
-                                                                "running with custom text writer");
-
-                               return _Writer.OutputStream;
-                       }
+               // We ignore the two properties on Mono as they are for use with IIS7, but there is
+               // no point in throwing PlatformNotSupportedException. We might find a use for them
+               // some day.
+               public int SubStatusCode {
+                       get;
+                       set;
                }
 
-#if NET_1_1
-               public string RedirectLocation {
-                       get { return redirectLocation; }
-                       set { redirectLocation = value; }
+               public bool TrySkipIisCustomErrors {
+                       get;
+                       set;
                }
-#endif
                
-               public string StatusDescription
-               {
+               public int StatusCode {
                        get {
-                               if (null == _sStatusDescription)
-                                       _sStatusDescription =
-                                               HttpWorkerRequest.GetStatusDescription (_iStatusCode);
-
-                               return _sStatusDescription;
+                               return status_code;
                        }
 
                        set {
-                               if (_bHeadersSent)
-                                       throw new HttpException ("Headers has been sent to the client");
-
-                               _sStatusDescription = value;
+                               if (headers_sent)
+                                       throw new HttpException ("headers have already been sent");
+                               
+                               status_code = value;
+                               status_description = null;
                        }
                }
-       
-               public int StatusCode
-               {
+
+               public string StatusDescription {
                        get {
-                               return _iStatusCode;
+                               if (status_description == null)
+                                       status_description = HttpWorkerRequest.GetStatusDescription (status_code);
+
+                               return status_description;
                        }
 
                        set {
-                               if (_bHeadersSent)
-                                       throw new HttpException ("Headers has been sent to the client");
-
-                               _sStatusDescription = null;
-                               _iStatusCode = value;
+                               if (headers_sent)
+                                       throw new HttpException ("headers have already been sent");
+                               
+                               status_description = value;
                        }
                }
-
-               public bool SuppressContent
-               {
+               
+               public bool SuppressContent {
                        get {
-                               return _bSuppressContent;
+                               return suppress_content;
                        }
-                       
+
                        set {
-                               _bSuppressContent = true;
+                               suppress_content = value;
                        }
                }
 
-               HttpRequest Request
+               [MonoTODO ("Not implemented")]
+               public void AddCacheDependency (CacheDependency[] dependencies)
                {
-                       get {
-                               if (_Context == null)
-                                       return null;
-
-                               return _Context.Request;
-                       }
+                       throw new NotImplementedException ();
                }
 
-               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;
-                       }
+               [MonoTODO ("Not implemented")]
+               public void AddCacheItemDependencies (string[] cacheKeys)
+               {
+                       throw new NotImplementedException ();
                }
 
-               public void AppendHeader (string name, string value)
+               [MonoTODO("Currently does nothing")]
+               public void AddCacheItemDependencies (ArrayList cacheKeys)
                {
-                       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;
-                               _bChunked = (value == "chunked");
-                               break;
-                       case "pragma":
-                               _sCacheControl = value;
-                               break;
-                       default:
-                               _Headers.Add (new HttpResponseHeader (name, value));
-                               break;
-                       }
+                       // TODO: talk to jackson about the cache
                }
-       
-               internal TextWriter SetTextWriter (TextWriter w)
+
+               [MonoTODO("Currently does nothing")]
+               public void AddCacheItemDependency (string cacheKey)
                {
-                       TextWriter prev = _TextWriter;
-                       _TextWriter = w;
-                       return prev;
+                       // TODO: talk to jackson about the cache
                }
                
-               public void BinaryWrite (byte [] buffer)
+               public void AddFileDependencies (ArrayList filenames)
                {
-                       OutputStream.Write (buffer, 0, buffer.Length);
+                       if (filenames == null || filenames.Count == 0)
+                               return;
+                       FileDependenciesArray.AddRange (filenames);
                }
 
-               internal void BinaryWrite (byte [] buffer, int start, int length)
-               {
-                       OutputStream.Write (buffer, start, length);
-               }
-               
-               public void Clear ()
+               public void AddFileDependencies (string[] filenames)
                {
-                       if (_Writer != null)
-                               _Writer.Clear ();
+                       if (filenames == null || filenames.Length == 0)
+                               return;
+                       FileDependenciesArray.AddRange (filenames);
                }
 
-               public void ClearContent ()
+               public void AddFileDependency (string filename)
                {
-                       Clear();
+                       if (filename == null || filename == String.Empty)
+                               return;
+                       FileDependenciesArray.Add (filename);
                }
 
-               internal void SetHeadersSent (bool val)
+               public void AddHeader (string name, string value)
                {
-                       _bHeadersSent = val;
+                       AppendHeader (name, value);
                }
-               
-               public void ClearHeaders ()
+
+               public void AppendCookie (HttpCookie cookie)
                {
-                       if (_bHeadersSent)
-                               throw new HttpException ("Headers has been sent to the client");
+                       Cookies.Add (cookie);
+               }
 
-                       _sContentType = "text/html";
-                       forced_charset = false;
+               //
+               // AppendHeader:
+               //    Special case for Content-Length, Content-Type, Transfer-Encoding and Cache-Control
+               //
+               //
+               public void AppendHeader (string name, string value)
+               {
+                       if (headers_sent)
+                               throw new HttpException ("Headers have been already sent");
+#if !TARGET_J2EE
+                       if (String.Compare (name, "content-length", true, Helpers.InvariantCulture) == 0){
+                               content_length = (long) UInt64.Parse (value);
+                               use_chunked = false;
+                               return;
+                       }
+#endif
 
-                       _iStatusCode = 200;
-                       _sCharset = null;
-                       _Headers = new ArrayList ();
-                       _sCacheControl = null;
-                       _sTransferEncoding = null;
+                       if (String.Compare (name, "content-type", true, Helpers.InvariantCulture) == 0){
+                               ContentType = value;
+                               return;
+                       }
 
-                       _lContentLength = 0;
-                       _bSuppressContent = false;
-                       _bSuppressHeaders = false;
-                       _bClientDisconnected = false;
-               }
+#if !TARGET_J2EE
+                       if (String.Compare (name, "transfer-encoding", true, Helpers.InvariantCulture) == 0){
+                               transfer_encoding = value;
+                               use_chunked = false;
+                               return;
+                       }
+#endif
 
-               public void Close ()
-               {
-                       if (closed && !_bClientDisconnected) {
-                               _bClientDisconnected = false;
-                               _WorkerRequest.CloseConnection ();
-                               _bClientDisconnected = true;
+                       if (String.Compare (name, "cache-control", true, Helpers.InvariantCulture) == 0){
+                               user_cache_control = value;
+                               return;
                        }
+
+                       Headers.Add (name, value);
                }
 
-               internal void Dispose ()
+               [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
+               public void AppendToLog (string param)
+               {
+                       Console.Write ("System.Web: ");
+                       Console.WriteLine (param);
+               }
+               
+               public string ApplyAppPathModifier (string virtualPath)
                {
-                       if (_Writer != null) {
-                               _Writer.Dispose ();
-                               _Writer = null;
+                       if (virtualPath == null || context == null)
+                               return null;
+               
+                       if (virtualPath.Length == 0)
+                               return context.Request.RootVirtualDir;
+
+                       if (UrlUtils.IsRelativeUrl (virtualPath)) {
+                               virtualPath = UrlUtils.Combine (context.Request.RootVirtualDir, virtualPath);
+                       } else if (UrlUtils.IsRooted (virtualPath)) {
+                               virtualPath = UrlUtils.Canonic (virtualPath);
+                       }
+
+                       bool cookieless = false;
+                       SessionStateSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/sessionState") as SessionStateSection;
+                       cookieless = SessionStateModule.IsCookieLess (context, config);
+
+                       if (!cookieless)
+                               return virtualPath;
+
+                       if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) < 0) {
+                               if (UrlUtils.HasSessionId (virtualPath))
+                                       virtualPath = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (virtualPath), virtualPath);
+                               return UrlUtils.InsertSessionId (app_path_mod, virtualPath);
                        }
+               
+                       return virtualPath;
                }
 
-               [MonoTODO("Handle callbacks into before done with session, needs to have a non ended flush here")]
-               internal void FlushAtEndOfRequest () 
+               public void BinaryWrite (byte [] buffer)
                {
-                       Flush (true);
+                       output_stream.Write (buffer, 0, buffer.Length);
                }
 
-               public void End ()
+               internal void BinaryWrite (byte [] buffer, int start, int len)
                {
-                       if (_bEnded)
-                               return;
+                       output_stream.Write (buffer, start, len);
+               }
 
-                       if (_Context.TimeoutPossible)
-                               Thread.CurrentThread.Abort (new StepCompleteRequest ());
+               public void Clear ()
+               {
+                       ClearContent ();
+               }
 
-                       Flush ();
-                       _bEnded = true;
-                       _Context.ApplicationInstance.CompleteRequest ();
+               public void ClearContent ()
+               {
+                       output_stream.Clear ();
+                       content_length = -1;
                }
 
-               public void Flush ()
+               public void ClearHeaders ()
                {
-                       if (closed)
-                               throw new HttpException ("Response already finished.");
+                       if (headers_sent)
+                               throw new HttpException ("headers have been already sent");
 
-                       Flush (false);
+                       // Reset the special case headers.
+                       content_length = -1;
+                       content_type = "text/html";
+                       transfer_encoding = null;
+                       user_cache_control = "private";
+                       if (cache_policy != null)
+                               cache_policy.Cacheability = HttpCacheability.Private;
+
+                       if (headers != null)
+                               headers.Clear ();
                }
 
-               private void Flush (bool bFinish)
+               internal bool HeadersSent {
+                       get {
+                               return headers_sent;
+                       }
+               }
+
+               public void Close ()
                {
-                       if (_bFlushing || closed)
+                       if (closed)
                                return;
+                       if (WorkerRequest != null)
+                               WorkerRequest.CloseConnection ();
+                       closed = true;
+               }
 
-                       _bFlushing = true;
-
-                       if (_Writer == null) {
-                               _TextWriter.Flush ();
-                               _bFlushing = false;
+               public void DisableKernelCache ()
+               {
+                       // does nothing in Mono
+               }
+               
+               public void End ()
+               {
+                       if (context == null)
                                return;
+                       
+                       if (context.TimeoutPossible) {
+                               Thread.CurrentThread.Abort (FlagEnd.Value);
+                       } else {
+                               // If this is called from an async event, signal the completion
+                               // but don't throw.
+                               HttpApplication app_instance = context.ApplicationInstance;
+                               if (app_instance != null)
+                                       app_instance.CompleteRequest ();
                        }
+               }
 
-                       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 ();
+               // Generate:
+               //   Content-Length
+               //   Content-Type
+               //   Transfer-Encoding (chunked)
+               //   Cache-Control
+               //   X-AspNet-Version
+               void AddHeadersNoCache (NameValueCollection write_headers, bool final_flush)
+               {
+#if !TARGET_J2EE
+                       //
+                       // Transfer-Encoding
+                       //
+                       if (use_chunked)
+                               write_headers.Add ("Transfer-Encoding", "chunked");
+                       else if (transfer_encoding != null)
+                               write_headers.Add ("Transfer-Encoding", transfer_encoding);
+#endif
+                       if (redirect_location != null)
+                               write_headers.Add ("Location", redirect_location);
+                       
+#if !TARGET_J2EE
+                       string vh = VersionHeader;
+                       if (vh != null)
+                               write_headers.Add ("X-AspNet-Version", vh);
+
+                       //
+                       // If Content-Length is set.
+                       //
+                       if (content_length >= 0) {
+                               write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
+                                                  content_length.ToString (Helpers.InvariantCulture));
+                       } else if (BufferOutput) {
+                               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 (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
+                                                          content_length.ToString (Helpers.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){
+                                               write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
                                        }
                                }
-
-                               if (!filtered) {
-                                       _Writer.FilterData (false);
-                                       length = _Writer.BufferSize;
+                       } else {
+                               //
+                               // If the content-length is not set, and we are not buffering, we must
+                               // close at the end.
+                               //
+                               if (use_chunked){
+                                       write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
                                }
+                       }
+#endif
 
-                               if (length == 0) {
-                                       if (bFinish && _bChunked) {
-                                               _WorkerRequest.SendResponseFromMemory (s_arrChunkEnd,
-                                                                               s_arrChunkEnd.Length);
+                       //
+                       // 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 ("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;
                                        }
-
-                                       _WorkerRequest.FlushResponse (bFinish);
-                                       if (!bFinish)
-                                               _Writer.Clear ();
-                                       return;
                                }
+                               
+                               write_headers.Add ("Content-Type", header);
+                       }
 
-                               if (!_bSuppressContent && Request.HttpMethod == "HEAD")
-                                       _bSuppressContent = true;
-
-                               if (_bSuppressContent)
-                                       _Writer.Clear ();
+                       if (cookies != null && cookies.Count != 0){
+                               int n = cookies.Count;
+                               for (int i = 0; i < n; i++)
+                                       write_headers.Add ("Set-Cookie", cookies.Get (i).GetCookieHeaderValue ());
+#if TARGET_J2EE
+                               // For J2EE Portal support emulate cookies by storing them in the session.
+                               context.Request.SetSessionCookiesForPortal (cookies);
+#endif
+                       }
+               }
 
-                               if (!_bSuppressContent) {
-                                       _bClientDisconnected = false;
-                                       if (_bChunked) {
-                                               Encoding oASCII = Encoding.ASCII;
+               internal void WriteHeaders (bool final_flush)
+               {
+                       if (headers_sent)
+                               return;
 
-                                               string chunk = Convert.ToString(_Writer.BufferSize, 16);
-                                               byte [] arrPrefix = oASCII.GetBytes (chunk + s_sChunkedPrefix);
+                       //
+                       // Flush
+                       //
+                       if (context != null) {
+                               HttpApplication app_instance = context.ApplicationInstance;
+                               if (app_instance != null)
+                                       app_instance.TriggerPreSendRequestHeaders ();
+                       }
 
-                                               _WorkerRequest.SendResponseFromMemory (arrPrefix,
-                                                                                      arrPrefix.Length);
+                       headers_sent = true;
 
-                                               _Writer.SendContent (_WorkerRequest);
+                       if (cached_response != null)
+                               cached_response.SetHeaders (headers);
 
-                                               _WorkerRequest.SendResponseFromMemory (s_arrChunkSuffix,
-                                                                                      s_arrChunkSuffix.Length);
-                                               if (bFinish)
-                                                       _WorkerRequest.SendResponseFromMemory (
-                                                                       s_arrChunkEnd, s_arrChunkEnd.Length);
-                                       } else {
-                                               _Writer.SendContent (_WorkerRequest);
+                       // If this page is cached use the cached headers
+                       // instead of the standard headers      
+                       NameValueCollection write_headers;
+                       if (cached_headers != null)
+                               write_headers = cached_headers;
+                       else {
+                               write_headers = Headers;
+                               AddHeadersNoCache (write_headers, final_flush);
+                       }
+                       
+                       if (WorkerRequest != null)
+                               WorkerRequest.SendStatus (status_code, StatusDescription);
+
+                       if (WorkerRequest != null) {
+                               string header_name;
+                               string[] values;
+                               int header_index;
+                               
+                               for (int i = 0; i < write_headers.Count; i++) {
+                                       header_name = write_headers.GetKey (i);
+                                       header_index = HttpWorkerRequest.GetKnownResponseHeaderIndex (header_name);
+                                       values = write_headers.GetValues (i);
+                                       if (values == null)
+                                               continue;
+                                       
+                                       foreach (string val in values) {
+                                               if (header_index > -1)
+                                                       WorkerRequest.SendKnownResponseHeader (header_index, val);
+                                               else
+                                                       WorkerRequest.SendUnknownResponseHeader (header_name, val);
                                        }
                                }
-
-                               _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 Pics (string value)
+               internal void DoFilter (bool close)
                {
-                       AppendHeader ("PICS-Label", value);
+                       if (output_stream.HaveFilter && context != null && context.Error == null)
+                               output_stream.ApplyFilter (close);
                }
 
+               internal void Flush (bool final_flush)
+               {
+                       DoFilter (final_flush);
+                       if (!headers_sent){
+                               if (final_flush || status_code != 200)
+                                       use_chunked = false;
+                       }
 
-               public void Redirect (string url)
+                       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;
+                       }
+
+                       if (!headers_sent)
+                               WriteHeaders (final_flush);
+
+                       if (context != null) {
+                               HttpApplication app_instance = context.ApplicationInstance;
+                               if (app_instance != null)
+                                       app_instance.TriggerPreSendRequestContent ();
+                       }
+
+                       if (IsCached)
+                               cached_response.SetData (output_stream.GetData ());
+
+                       if (WorkerRequest != null)
+                               output_stream.Flush (WorkerRequest, final_flush);
+               }
+
+               public void Flush ()
                {
-                       Redirect (url, true);
+                       Flush (false);
                }
 
-               public void Redirect (string url, bool endResponse)
+               public void Pics (string value)
                {
-                       if (_bHeadersSent)
-                               throw new HttpException ("Headers has been sent to the client");
+                       AppendHeader ("PICS-Label", value);
+               }
 
-                       Clear ();
+               void Redirect (string url, bool endResponse, int code)
+               {
+                       if (url == null)
+                               throw new ArgumentNullException ("url");
+                       
+                       if (headers_sent)
+                               throw new HttpException ("Headers have already been sent");
 
+                       if (url.IndexOf ('\n') != -1)
+                               throw new ArgumentException ("Redirect URI cannot contain newline characters.", "url");
+                       
+                       is_request_being_redirected = true;
+                       ClearHeaders ();
+                       ClearContent ();
+                       
+                       StatusCode = code;
                        url = ApplyAppPathModifier (url);
-                       StatusCode = 302;
-                       AppendHeader (HttpWorkerRequest.HeaderLocation, url);
+
+                       bool isFullyQualified;
+                       if (StrUtils.StartsWith (url, "http:", true) ||
+                           StrUtils.StartsWith (url, "https:", true) ||
+                           StrUtils.StartsWith (url, "file:", true) ||
+                           StrUtils.StartsWith (url, "ftp:", true))
+                               isFullyQualified = true;
+                       else
+                               isFullyQualified = false;
+
+                       if (!isFullyQualified) {
+                               HttpRuntimeSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime") as HttpRuntimeSection;
+                               if (config != null && config.UseFullyQualifiedRedirectUrl) {
+                                       var ub = new UriBuilder (context.Request.Url);
+                                       ub.Path = url;
+                                       ub.Fragment = null;
+                                       ub.Password = null;
+                                       ub.Query = null;
+                                       ub.UserName = null;
+                                       url = ub.Uri.ToString ();
+                               }
+                       }
+                       
+                       redirect_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 ("<h2>Object moved to <a href=\"" + url + "\">here</a></h2>\r\n");
                        Write ("</body><html>\r\n");
-
+                       
                        if (endResponse)
                                End ();
+                       is_request_being_redirected = false;
+               }
+               
+               public void Redirect (string url)
+               {
+                       Redirect (url, true);
                }
 
-               internal bool RedirectCustomError (string errorPage)
+               public void Redirect (string url, bool endResponse)
                {
-                       if (_bHeadersSent)
-                               return false;
+                       Redirect (url, endResponse, 302);
+               }
+#if NET_4_0
+               public void RedirectPermanent (string url)
+               {
+                       RedirectPermanent (url, true);
+               }
 
-                       if (Request.QueryString ["aspxerrorpath"] != null)
-                               return false; // Prevent endless loop
+               public void RedirectPermanent (string url, bool endResponse)
+               {
+                       Redirect (url, endResponse, 301);
+               }
 
-                       Redirect (errorPage + "?aspxerrorpath=" + Request.Path, false);
-                       return true;
+               public void RedirectToRoute (object routeValues)
+               {
+                       RedirectToRoute ("RedirectToRoute", null, new RouteValueDictionary (routeValues), 302, true);
                }
-               
-               public void Write (char ch)
+
+               public void RedirectToRoute (RouteValueDictionary routeValues)
                {
-                       _TextWriter.Write(ch);
+                       RedirectToRoute ("RedirectToRoute", null, routeValues, 302, true);
                }
 
-               public void Write (object obj)
+               public void RedirectToRoute (string routeName)
                {
-                       _TextWriter.Write(obj);
+                       RedirectToRoute ("RedirectToRoute", routeName, null, 302, true);
                }
 
-               public void Write (string str)
+               public void RedirectToRoute (string routeName, object routeValues)
                {
-                       _TextWriter.Write (str);
+                       RedirectToRoute ("RedirectToRoute", routeName, new RouteValueDictionary (routeValues), 302, true);
                }
 
-               public void Write (char [] buffer, int index, int count)
+               public void RedirectToRoute (string routeName, RouteValueDictionary routeValues)
+               {
+                       RedirectToRoute ("RedirectToRoute", routeName, routeValues, 302, true);
+               }
+
+               public void RedirectToRoutePermanent (object routeValues)
+               {
+                       RedirectToRoute ("RedirectToRoutePermanent", null, new RouteValueDictionary (routeValues), 301, false);
+               }
+
+               public void RedirectToRoutePermanent (RouteValueDictionary routeValues)
+               {
+                       RedirectToRoute ("RedirectToRoutePermanent", null, routeValues, 301, false);
+               }
+
+               public void RedirectToRoutePermanent (string routeName)
+               {
+                       RedirectToRoute ("RedirectToRoutePermanent", routeName, null, 301, false);
+               }
+
+               public void RedirectToRoutePermanent (string routeName, object routeValues)
+               {
+                       RedirectToRoute ("RedirectToRoutePermanent", routeName, new RouteValueDictionary (routeValues), 301, false);
+               }               
+
+               public void RedirectToRoutePermanent (string routeName, RouteValueDictionary routeValues)
                {
-                       _TextWriter.Write (buffer, index, count);
+                       RedirectToRoute ("RedirectToRoutePermanent", routeName, routeValues, 301, false);
                }
+               
+               void RedirectToRoute (string callerName, string routeName, RouteValueDictionary routeValues, int redirectCode, bool endResponse)
+               {
+                       HttpContext ctx = context ?? HttpContext.Current;
+                       HttpRequest req = ctx != null ? ctx.Request : null;
+                       
+                       if (req == null)
+                               // Let's emulate .NET
+                               throw new NullReferenceException ();
+                       
+                       VirtualPathData vpd = RouteTable.Routes.GetVirtualPath (req.RequestContext, routeName, routeValues);
+                       string redirectUrl = vpd != null ? vpd.VirtualPath : null;
+                       if (String.IsNullOrEmpty (redirectUrl))
+                               throw new InvalidOperationException ("No matching route found for RedirectToRoute");
+
+                       Redirect (redirectUrl, true, redirectCode);
+               }
+
+               public static void RemoveOutputCacheItem (string path, string providerName)
+               {
+                       if (path == null)
+                               throw new ArgumentNullException ("path");
+
+                       if (path.Length > 0 && path [0] != '/')
+                               throw new ArgumentException ("Invalid path for HttpResponse.RemoveOutputCacheItem: '" + path + "'. An absolute virtual path is expected");
 
+                       OutputCache.RemoveFromProvider (path, providerName);
+               }
+#endif
                public static void RemoveOutputCacheItem (string path)
                {
                        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.");
 
-                       Cache cache = HttpRuntime.Cache;
-                       cache.Remove (path);
+                       if (path.Length == 0)
+                               return;
+
+                       if (path [0] != '/')
+                               throw new ArgumentException ("'" + path + "' is not an absolute virtual path.");
+
+#if NET_4_0
+                       RemoveOutputCacheItem (path, OutputCache.DefaultProviderName);
+#else
+                       HttpContext context = HttpContext.Current;
+                       HttpApplication app_instance = context != null ? context.ApplicationInstance : null;
+                       HttpModuleCollection modules = app_instance != null ? app_instance.Modules : null;
+                       OutputCacheModule ocm = modules != null ? modules.Get ("OutputCache") as OutputCacheModule : null;
+                       OutputCacheProvider internalProvider = ocm != null ? ocm.InternalProvider : null;
+                       if (internalProvider == null)
+                               return;
+
+                       internalProvider.Remove (path);
+#endif
                }
 
                public void SetCookie (HttpCookie cookie)
                {
-                       if (_bHeadersSent)
-                               throw new HttpException ("Cannot append cookies after HTTP headers have been sent");
+                       AppendCookie (cookie);
+               }
 
-                       Cookies.Add (cookie);
+               public void Write (char ch)
+               {
+                       TextWriter writer = Output;
+#if NET_4_0
+                       // Emulating .NET
+                       if (writer == null)
+                               throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
+#endif
+                       writer.Write (ch);
                }
 
-               private void WriteFromStream (Stream stream, long offset, long length, long bufsize)
+               public void Write (object obj)
                {
-                       if (offset < 0 || length <= 0)
+                       TextWriter writer = Output;
+#if NET_4_0
+                       // Emulating .NET
+                       if (writer == null)
+                               throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
+#endif
+                       if (obj == null)
                                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);
-                       }
+                       writer.Write (obj.ToString ());
+               }
+               
+               public void Write (string s)
+               {
+                       TextWriter writer = Output;
+#if NET_4_0
+                       // Emulating .NET
+                       if (writer == null)
+                               throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
+#endif
+                       writer.Write (s);
+               }
+               
+               public void Write (char [] buffer, int index, int count)
+               {
+                       TextWriter writer = Output;
+#if NET_4_0
+                       // Emulating .NET
+                       if (writer == null)
+                               throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
+#endif
+                       writer.Write (buffer, index, count);
                }
 
+               internal void WriteFile (FileStream fs, long offset, long size)
+               {
+                       byte [] buffer = new byte [32*1024];
+
+                       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);
@@ -1012,63 +1093,245 @@ namespace System.Web
 
                public void WriteFile (string filename, bool readIntoMemory)
                {
-                       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 ();
+                       if (filename == null)
+                               throw new ArgumentNullException ("filename");
+
+                       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 TARGET_JVM
+               public void WriteFile (IntPtr fileHandle, long offset, long size) {
+                       throw new PlatformNotSupportedException("IntPtr not supported");
+               }
+#else
+               public void WriteFile (IntPtr fileHandle, long offset, long size)
+               {
+                       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
 
                public void WriteFile (string filename, long offset, long size)
                {
-                       FileStream fs = null;
-                       try {
-                               fs = File.OpenRead (filename);
-                               WriteFromStream (fs, offset, size, 8192);
-                       } finally {
-                               if (fs != null)
-                                       fs.Close ();
-                       }
+                       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;
+                       
+                       FileStream fs = File.OpenRead (filename);
+                       WriteFile (fs, offset, size);
+
+                       if (buffer)
+                               return;
+
+                       output_stream.ApplyFilter (false);
+                       Flush ();
                }
 
-               public void WriteFile (IntPtr fileHandle, long offset, long size)
+               public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
                {
-                       FileStream fs = null;
-                       try {
-                               fs = new FileStream (fileHandle, FileAccess.Read);
-                               WriteFromStream (fs, offset, size, 8192);
-                       } finally {
-                               if (fs != null)
-                                       fs.Close ();
+                       // Emulation of .NET behavior
+                       if (callback == null)
+                               throw new NullReferenceException ();
+
+                       object target = callback.Target;
+                       if (target != null && target.GetType () == typeof (Control))
+                               throw new ArgumentException ("callback");
+
+                       string s = callback (context);
+                       if (!IsCached) {
+                               Write (s);
+                               return;
                        }
-               }   
 
-               [MonoTODO()]
-               internal void OnCookieAdd (HttpCookie cookie)
+                       Cache.Cacheability = HttpCacheability.Server;
+                       Flush ();
+                       if (WorkerRequest == null)
+                               Write (s); // better this than nothing
+                       else {
+                               byte[] bytes = WebEncoding.ResponseEncoding.GetBytes (s);
+                               WorkerRequest.SendResponseFromMemory (bytes, bytes.Length);
+                       }
+                       
+                       cached_response.SetData (callback);
+               }
+
+               //
+               // 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);
+               }
+
+               internal void TransmitFile (string filename, bool final_flush)
+               {
+                       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 TransmitFile (string filename, long offset, long length)
+               {
+                       output_stream.WriteFile (filename, offset, length);
+                       output_stream.ApplyFilter (false);
+                       Flush (false);
+               }
+               
+               internal void TransmitFile (VirtualFile vf)
+               {
+                       TransmitFile (vf, false);
+               }
+
+               const int bufLen = 65535;
+               internal void TransmitFile (VirtualFile vf, bool final_flush)
+               {
+                       if (vf == null)
+                               throw new ArgumentNullException ("vf");
+
+                       if (vf is DefaultVirtualFile) {
+                               TransmitFile (HostingEnvironment.MapPath (vf.VirtualPath), final_flush);
+                               return;
+                       }
+                       
+                       byte[] buf = new byte [bufLen];
+                       using (Stream s = vf.Open ()) {
+                               int readBytes;
+                               while ((readBytes = s.Read (buf, 0, bufLen)) > 0) {
+                                       output_stream.Write (buf, 0, readBytes);
+                                       output_stream.ApplyFilter (final_flush);
+                                       Flush (false);
+                               }
+                               if (final_flush)
+                                       Flush (true);
+                       }
+               }
+               
+#region Session state support
+               internal void SetAppPathModifier (string app_modifier)
                {
+                       app_path_mod = app_modifier;
+               }
+#endregion
+               
+#region Cache Support
+               internal void SetCachedHeaders (NameValueCollection headers)
+               {
+                       cached_headers = headers;
+                       
+               }
+
+               internal bool IsCached {
+                       get { return cached_response != null; }
+                       set {
+                               if (value)
+                                       cached_response = new CachedRawResponse (cache_policy);
+                               else
+                                       cached_response = null;
+                       }
                }
 
-               [MonoTODO("Do we need this?")]
-               internal void OnCookieChange (HttpCookie cookie)
+               public HttpCachePolicy Cache {
+                       get {
+                               if (cache_policy == null)
+                                       cache_policy = new HttpCachePolicy ();
+                               
+                               return cache_policy;
+                       }
+               }               
+
+               internal CachedRawResponse GetCachedResponse ()
                {
+                       if (cached_response != null) {
+                               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, Helpers.InvariantCulture) == 0) {
+                                       Cache.SetCacheability (HttpCacheability.Public);
+                                       user_cache_control = "public";
+                               } else if (String.Compare (value, "private", true, Helpers.InvariantCulture) == 0) {
+                                       Cache.SetCacheability (HttpCacheability.Private);
+                                       user_cache_control = "private";
+                               } else if (String.Compare (value, "no-cache", true, Helpers.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;
                }
        }
+
+#if TARGET_J2EE
+       public 
+#endif 
+       static class FlagEnd
+       {
+               public static readonly object Value = new object ();
+       }
 }
+