Call SetEndOfSendNotification
[mono.git] / mcs / class / System.Web / System.Web / HttpResponse.cs
index 24b5f28be54c01c101587ac27a6e6d75ef5ce4b8..9aa99d9244bba7f1fefdf3e5dd6a763be74ae5a0 100644 (file)
@@ -5,8 +5,9 @@
 // Author:
 //     Miguel de Icaza (miguel@novell.com)
 //     Gonzalo Paniagua Javier (gonzalo@ximian.com)
+//      Marek Habersack <mhabersack@novell.com>
 //
-// Copyright (C) 2005 Novell, Inc (http://www.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
@@ -42,11 +43,16 @@ using System.Security.Permissions;
 using System.Web.Hosting;
 using System.Web.SessionState;
 
-namespace System.Web {
-       
+#if NET_4_0
+using System.Web.Routing;
+#endif
+
+namespace System.Web
+{      
        // CAS - no InheritanceDemand here as the class is sealed
        [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
-       public sealed partial class HttpResponse {
+       public sealed partial class HttpResponse
+       {
                internal HttpWorkerRequest WorkerRequest;
                internal HttpResponseStream output_stream;
                internal bool buffer = true;
@@ -82,7 +88,7 @@ namespace System.Web {
                // the headers that we compute here.
                //
 
-               NameValueCollection headers;
+               HttpHeaderCollection headers;
                bool headers_sent;
                NameValueCollection cached_headers;
 
@@ -99,19 +105,18 @@ namespace System.Web {
                // Session State
                //
                string app_path_mod;
-               
-#if NET_2_0
                bool is_request_being_redirected;
                Encoding headerEncoding;
-#endif
 
                internal HttpResponse ()
                {
                        output_stream = new HttpResponseStream (this);
+                       writer = new HttpWriter (this);
                }
 
                public HttpResponse (TextWriter writer) : this ()
                {
+
                        this.writer = writer;
                }
 
@@ -124,8 +129,9 @@ namespace System.Web {
                        if (worker_request != null)
                                use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1");
 #endif
+                       writer = new HttpWriter (this);
                }
-               
+
                internal TextWriter SetTextWriter (TextWriter writer)
                {
                        TextWriter prev = this.writer;
@@ -137,11 +143,7 @@ namespace System.Web {
                        get {
                                if (!version_header_checked && version_header == null) {
                                        version_header_checked = true;
-#if NET_2_0
                                        HttpRuntimeSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime") as HttpRuntimeSection;
-#else
-                                       HttpRuntimeConfig config = HttpContext.GetAppConfig ("system.web/httpRuntime") as HttpRuntimeConfig;
-#endif
                                        if (config != null && config.EnableVersionHeader)
                                                version_header = Environment.Version.ToString (3);
                                }
@@ -149,7 +151,12 @@ namespace System.Web {
                                return version_header;
                        }
                }
-               
+
+               internal HttpContext Context {
+                       get { return context; }
+                       set { context = value; }
+               }
+                       
                internal string[] FileDependencies {
                        get {
                                if (fileDependencies == null || fileDependencies.Count == 0)
@@ -287,7 +294,7 @@ namespace System.Web {
                                output_stream.Filter = value;
                        }
                }
-#if NET_2_0
+
                public Encoding HeaderEncoding {
                        get {
                                if (headerEncoding == null) {
@@ -314,14 +321,10 @@ namespace System.Web {
                        }
                }
 
-               public
-#else
-               internal
-#endif
-               NameValueCollection Headers {
+               public NameValueCollection Headers {
                        get {
                                if (headers == null)
-                                       headers = new NameValueCollection ();
+                                       headers = new HttpHeaderCollection ();
 
                                return headers;
                        }
@@ -336,18 +339,18 @@ namespace System.Web {
                                return WorkerRequest.IsClientConnected ();
                        }
                }
-#if NET_2_0
+
                public bool IsRequestBeingRedirected {
                        get { return is_request_being_redirected; }
                }
-#endif
+
                public TextWriter Output {
                        get {
-                               if (writer == null)
-                                       writer = new HttpWriter (this);
-
                                return writer;
                        }
+#if NET_4_0
+                       set { writer = value; }
+#endif
                }
 
                public Stream OutputStream {
@@ -376,23 +379,13 @@ namespace System.Web {
 
                                string s = value.Substring (0, p);
                                
-#if NET_2_0
                                if (!Int32.TryParse (s, out status_code))
                                        throw new HttpException ("Invalid format for the Status property");
-#else
-                                                   
-                               try {
-                                       status_code = Int32.Parse (s);
-                               } catch {
-                                       throw new HttpException ("Invalid format for the Status property");
-                               }
-#endif
                                
                                status_description = value.Substring (p+1);
                        }
                }
 
-#if NET_2_0
                // 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.
@@ -405,7 +398,6 @@ namespace System.Web {
                        get;
                        set;
                }
-#endif
                
                public int StatusCode {
                        get {
@@ -447,7 +439,6 @@ namespace System.Web {
                        }
                }
 
-#if NET_2_0
                [MonoTODO ("Not implemented")]
                public void AddCacheDependency (CacheDependency[] dependencies)
                {
@@ -459,7 +450,7 @@ namespace System.Web {
                {
                        throw new NotImplementedException ();
                }
-#endif
+
                [MonoTODO("Currently does nothing")]
                public void AddCacheItemDependencies (ArrayList cacheKeys)
                {
@@ -478,14 +469,13 @@ namespace System.Web {
                                return;
                        FileDependenciesArray.AddRange (filenames);
                }
-#if NET_2_0
+
                public void AddFileDependencies (string[] filenames)
                {
                        if (filenames == null || filenames.Length == 0)
                                return;
                        FileDependenciesArray.AddRange (filenames);
                }
-#endif         
 
                public void AddFileDependency (string filename)
                {
@@ -512,8 +502,7 @@ namespace System.Web {
                public void AppendHeader (string name, string value)
                {
                        if (headers_sent)
-                               throw new HttpException ("headers have been already 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);
@@ -552,7 +541,7 @@ namespace System.Web {
                
                public string ApplyAppPathModifier (string virtualPath)
                {
-                       if (virtualPath == null)
+                       if (virtualPath == null || context == null)
                                return null;
                
                        if (virtualPath.Length == 0)
@@ -565,13 +554,9 @@ namespace System.Web {
                        }
 
                        bool cookieless = false;
-#if NET_2_0
                        SessionStateSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/sessionState") as SessionStateSection;
                        cookieless = SessionStateModule.IsCookieLess (context, config);
-#else
-                       SessionConfig config = HttpContext.GetAppConfig ("system.web/sessionState") as SessionConfig;
-                       cookieless = config.CookieLess;
-#endif
+
                        if (!cookieless)
                                return virtualPath;
 
@@ -637,12 +622,10 @@ namespace System.Web {
                        closed = true;
                }
 
-#if NET_2_0
                public void DisableKernelCache ()
                {
                        // does nothing in Mono
                }
-#endif
                
                public void End ()
                {
@@ -843,11 +826,8 @@ namespace System.Web {
                                        app_instance.TriggerPreSendRequestContent ();
                        }
 
-                       if (IsCached) {
-                               MemoryStream ms = output_stream.GetData ();
-                               cached_response.ContentLength = (int) ms.Length;
-                               cached_response.SetData (ms.GetBuffer ());
-                       }
+                       if (IsCached)
+                               cached_response.SetData (output_stream.GetData ());
 
                        if (WorkerRequest != null)
                                output_stream.Flush (WorkerRequest, final_flush);
@@ -863,36 +843,46 @@ namespace System.Web {
                        AppendHeader ("PICS-Label", value);
                }
 
-               public void Redirect (string url)
-               {
-                       Redirect (url, true);
-               }
-
-               public void Redirect (string url, bool endResponse)
+               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 NET_2_0
+                       if (url.IndexOf ('\n') != -1)
+                               throw new ArgumentException ("Redirect URI cannot contain newline characters.", "url");
+                       
                        is_request_being_redirected = true;
-#endif
                        ClearHeaders ();
                        ClearContent ();
                        
-                       StatusCode = 302;
+                       StatusCode = code;
                        url = ApplyAppPathModifier (url);
-#if NET_2_0
-                       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 ();
+
+                       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 ();
+                               }
                        }
-#endif
+                       
                        redirect_location = url;
 
                        // Text for browsers that can't handle location header
@@ -902,11 +892,107 @@ namespace System.Web {
                        
                        if (endResponse)
                                End ();
-#if NET_2_0
                        is_request_being_redirected = false;
-#endif
+               }
+               
+               public void Redirect (string url)
+               {
+                       Redirect (url, true);
+               }
+
+               public void Redirect (string url, bool endResponse)
+               {
+                       Redirect (url, endResponse, 302);
+               }
+#if NET_4_0
+               public void RedirectPermanent (string url)
+               {
+                       RedirectPermanent (url, true);
+               }
+
+               public void RedirectPermanent (string url, bool endResponse)
+               {
+                       Redirect (url, endResponse, 301);
                }
 
+               public void RedirectToRoute (object routeValues)
+               {
+                       RedirectToRoute ("RedirectToRoute", null, new RouteValueDictionary (routeValues), 302, true);
+               }
+
+               public void RedirectToRoute (RouteValueDictionary routeValues)
+               {
+                       RedirectToRoute ("RedirectToRoute", null, routeValues, 302, true);
+               }
+
+               public void RedirectToRoute (string routeName)
+               {
+                       RedirectToRoute ("RedirectToRoute", routeName, null, 302, true);
+               }
+
+               public void RedirectToRoute (string routeName, object routeValues)
+               {
+                       RedirectToRoute ("RedirectToRoute", routeName, new RouteValueDictionary (routeValues), 302, true);
+               }
+
+               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)
+               {
+                       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)
@@ -918,7 +1004,19 @@ namespace System.Web {
                        if (path [0] != '/')
                                throw new ArgumentException ("'" + path + "' is not an absolute virtual path.");
 
-                       HttpRuntime.InternalCache.Remove (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)
@@ -928,25 +1026,49 @@ namespace System.Web {
 
                public void Write (char ch)
                {
-                       Output.Write (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);
                }
 
                public void Write (object obj)
                {
+                       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;
                        
-                       Output.Write (obj.ToString ());
+                       writer.Write (obj.ToString ());
                }
                
                public void Write (string s)
                {
-                       Output.Write (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)
                {
-                       Output.Write (buffer, index, 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)
@@ -1036,13 +1158,35 @@ namespace System.Web {
                        output_stream.ApplyFilter (false);
                        Flush ();
                }
-#if NET_2_0
-               [MonoTODO ("Not implemented")]
+
                public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
                {
-                       throw new NotImplementedException ();
+                       // 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;
+                       }
+
+                       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);
                }
-#endif
+
                //
                // Like WriteFile, but never buffers, so we manually Flush here
                //
@@ -1063,7 +1207,6 @@ namespace System.Web {
                        Flush (final_flush);
                }
 
-#if NET_2_0
                public void TransmitFile (string filename, long offset, long length)
                {
                        output_stream.WriteFile (filename, offset, length);
@@ -1099,7 +1242,6 @@ namespace System.Web {
                                        Flush (true);
                        }
                }
-#endif
                
 #region Session state support
                internal void SetAppPathModifier (string app_modifier)