New test.
[mono.git] / mcs / class / System.Web / System.Web / HttpResponse.cs
index 09d002bb4faa890b30ad1eb574728a6ffbd925d3..890078fb4a179bffa4567190965a291a6c06bc51 100644 (file)
@@ -5,8 +5,6 @@
 // Author:
 //     Miguel de Icaza (miguel@novell.com)
 //     Gonzalo Paniagua Javier (gonzalo@ximian.com)
-//
-
 //
 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
 //
@@ -29,6 +27,7 @@
 // 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.Text;
 using System.Web.UI;
 using System.Collections;
@@ -38,9 +37,12 @@ using System.Web.Caching;
 using System.Threading;
 using System.Web.Util;
 using System.Globalization;
+using System.Security.Permissions;
 
 namespace System.Web {
        
+       // CAS - no InheritanceDemand here as the class is sealed
+       [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        public sealed class HttpResponse {
                internal HttpWorkerRequest WorkerRequest;
                internal HttpResponseStream output_stream;
@@ -59,7 +61,7 @@ namespace System.Web {
                string charset;
                bool charset_set;
                CachedRawResponse cached_response;
-               string cache_control = "private";
+               string user_cache_control = "private";
                string redirect_location;
                
                //
@@ -80,7 +82,7 @@ namespace System.Web {
                // Transfer encoding state
                //
                string transfer_encoding;
-               internal byte [] use_chunked;
+               internal bool use_chunked;
                
                bool closed;
                internal bool suppress_content;
@@ -95,8 +97,6 @@ namespace System.Web {
                //
                internal object FlagEnd = new object ();
 
-               internal readonly static byte [] ChunkedNewline = new byte [2] { 13, 10 };
-               
                internal HttpResponse ()
                {
                        output_stream = new HttpResponseStream (this);
@@ -113,7 +113,7 @@ namespace System.Web {
                        this.context = context;
 
                        if (worker_request != null)
-                               use_chunked = worker_request.GetHttpVersion () == "HTTP/1.1" ? new byte [24] : null;
+                               use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1");
                }
                
                internal TextWriter SetTextWriter (TextWriter writer)
@@ -150,17 +150,19 @@ namespace System.Web {
                //
                public Encoding ContentEncoding {
                        get {
-                               if (encoding == 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 {
-                                                       encoding = WebEncoding.ResponseEncoding;
+                               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 {
+                                                       }
                                                }
-                                       } else
+                                       }
+                                       if (encoding == null)
                                                encoding = WebEncoding.ResponseEncoding;
                                }
                                return encoding;
@@ -231,23 +233,44 @@ namespace System.Web {
                                Cache.SetExpires (value);
                        }
                }
-               
+
                public Stream Filter {
                        get {
-                               throw new NotImplementedException ();
+                               if (WorkerRequest == null)
+                                       return null;
+
+                               return output_stream.Filter;
                        }
 
                        set {
+                               output_stream.Filter = value;
+                       }
+               }
+#if NET_2_0
+               [MonoTODO]
+               public Encoding HeaderEncoding {
+                       get { throw new NotImplementedException (); }
+                       set {
+                               if (value == null)
+                                       throw new ArgumentNullException ("HeaderEncoding");
                                throw new NotImplementedException ();
                        }
                }
-               
+#endif
                public bool IsClientConnected {
                        get {
+                               if (WorkerRequest == null)
+                                       return true; // yep that's true
+
                                return WorkerRequest.IsClientConnected ();
                        }
                }
-               
+#if NET_2_0
+               [MonoTODO]
+               public bool IsRequestBeingRedirected {
+                       get { throw new NotImplementedException (); }
+               }
+#endif
                public TextWriter Output {
                        get {
                                if (writer == null)
@@ -340,22 +363,44 @@ namespace System.Web {
                                suppress_content = value;
                        }
                }
+#if NET_2_0
+               [MonoTODO]
+               public void AddCacheDependency (CacheDependency[] dependencies)
+               {
+                       throw new NotImplementedException ();
+               }
 
+               [MonoTODO]
+               public void AddCacheItemDependencies (string[] cacheKeys)
+               {
+                       throw new NotImplementedException ();
+               }
+#endif
+               [MonoTODO]
                public void AddCacheItemDependencies (ArrayList cacheKeys)
                {
                        // TODO: talk to jackson about the cache
                }
 
+               [MonoTODO]
                public void AddCacheItemDependency (string cacheKey)
                {
                        // TODO: talk to jackson about the cache
                }
 
+               [MonoTODO]
                public void AddFileDependencies (ArrayList filenames)
                {
                        // TODO: talk to jackson about the cache
                }
-
+#if NET_2_0
+               [MonoTODO]
+               public void AddFileDependencies (string[] filenames)
+               {
+                       throw new NotImplementedException ();
+               }
+#endif
+               [MonoTODO]
                public void AddFileDependency (string filename)
                {
                        // TODO: talk to jackson about the cache
@@ -382,8 +427,8 @@ namespace System.Web {
                                throw new HttpException ("headers have been already sent");
                        
                        if (String.Compare (name, "content-length", true, CultureInfo.InvariantCulture) == 0){
-                               content_length = Int64.Parse (value);
-                               use_chunked = null;
+                               content_length = (long) UInt64.Parse (value);
+                               use_chunked = false;
                                return;
                        }
 
@@ -394,18 +439,19 @@ namespace System.Web {
 
                        if (String.Compare (name, "transfer-encoding", true, CultureInfo.InvariantCulture) == 0){
                                transfer_encoding = value;
-                               use_chunked = null;
+                               use_chunked = false;
                                return;
                        }
 
                        if (String.Compare (name, "cache-control", true, CultureInfo.InvariantCulture) == 0){
-                               cache_control = value;
+                               user_cache_control = value;
                                return;
                        }
 
                        headers.Add (new UnknownResponseHeader (name, value));
                }
 
+               [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
                public void AppendToLog (string param)
                {
                        Console.Write ("System.Web: ");
@@ -439,37 +485,9 @@ namespace System.Web {
                        return virtualPath;
                }
 
-               internal void SendSize (long l)
-               {
-                       string s = l.ToString ();
-                       int i;
-                       
-                       for (i = 0; i < s.Length; i++){
-                               use_chunked [i] = (byte) s [i];
-                       }
-                       use_chunked [i++] = 13;
-                       use_chunked [i++] = 10;
-
-                       WorkerRequest.SendResponseFromMemory (use_chunked, i);
-               }
-               
                public void BinaryWrite (byte [] buffer)
                {
-                       if (this.buffer)
-                               output_stream.Write (buffer, 0, buffer.Length);
-                       else {
-                               if (use_chunked != null)
-                                       SendSize (buffer.Length);
-                                               
-                               WorkerRequest.SendResponseFromMemory (buffer, buffer.Length);
-
-                               WorkerRequest.SendResponseFromMemory (ChunkedNewline, 2);
-                               
-                               //
-                               // TODO This hack is for current XSP
-                               //
-                               WorkerRequest.FlushResponse (false);
-                       }
+                       output_stream.Write (buffer, 0, buffer.Length);
                }
 
                internal void BinaryWrite (byte [] buffer, int start, int len)
@@ -496,7 +514,7 @@ namespace System.Web {
                        content_length = -1;
                        content_type = "text/html";
                        transfer_encoding = null;
-                       cache_control = "private";
+                       user_cache_control = null;
                        headers.Clear ();
                }
 
@@ -510,7 +528,8 @@ namespace System.Web {
                {
                        if (closed)
                                return;
-                       WorkerRequest.CloseConnection ();
+                       if (WorkerRequest != null)
+                               WorkerRequest.CloseConnection ();
                        closed = true;
                }
 
@@ -520,7 +539,7 @@ namespace System.Web {
                                Thread.CurrentThread.Abort (FlagEnd);
                        } else {
                                // If this is called from an async event, signal the completion
-                               // but don't thow.
+                               // but don't throw.
                                context.ApplicationInstance.CompleteRequest ();
                        }
                }
@@ -530,23 +549,12 @@ namespace System.Web {
                //   Content-Type
                //   Transfer-Encoding (chunked)
                //   Cache-Control
-               void WriteHeaders (bool final_flush)
+               void AddHeadersNoCache (ArrayList write_headers, bool final_flush)
                {
-                       WorkerRequest.SendStatus (status_code, StatusDescription);
-
-                       if (cached_response != null)
-                               cached_response.SetHeaders (headers);
-
-                       // If this page is cached use the cached headers
-                       // instead of the standard headers      
-                       ArrayList write_headers = headers;
-                       if (cached_headers != null)
-                               write_headers = cached_headers;
-
                        //
                        // Transfer-Encoding
                        //
-                       if (use_chunked != null)
+                       if (use_chunked)
                                write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", "chunked"));
                        else if (transfer_encoding != null)
                                write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", transfer_encoding));
@@ -581,8 +589,10 @@ namespace System.Web {
                                        // 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 == null){
+                                       if (use_chunked){
+#if DEBUG
                                                Console.WriteLine ("Setting to close2");
+#endif
                                                write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
                                        }
                                }
@@ -591,8 +601,10 @@ namespace System.Web {
                                // If the content-length is not set, and we are not buffering, we must
                                // close at the end.
                                //
-                               if (use_chunked == null){
+                               if (use_chunked){
+#if DEBUG
                                        Console.WriteLine ("Setting to close");
+#endif
                                        write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
                                }
                        }
@@ -603,7 +615,7 @@ namespace System.Web {
                        if (cache_policy != null)
                                cache_policy.SetHeaders (this, headers);
                        else
-                               write_headers.Add (new UnknownResponseHeader ("Cache-Control", cache_control));
+                               write_headers.Add (new UnknownResponseHeader ("Cache-Control", CacheControl));
                        
                        //
                        // Content-Type
@@ -628,49 +640,84 @@ namespace System.Web {
                                        write_headers.Add (cookies.Get (i).GetCookieHeader ());
                        }
                        
+               }
+
+               internal void WriteHeaders (bool final_flush)
+               {
+                       if (headers_sent)
+                               return;
+
+                       if (WorkerRequest != null)
+                               WorkerRequest.SendStatus (status_code, StatusDescription);
+
+                       if (cached_response != null)
+                               cached_response.SetHeaders (headers);
+
+                       // If this page is cached use the cached headers
+                       // instead of the standard headers      
+                       ArrayList write_headers = headers;
+                       if (cached_headers != null)
+                               write_headers = cached_headers;
+                       else
+                               AddHeadersNoCache (write_headers, final_flush);
+
                        //
                        // Flush
                        //
-                       HttpApplication app_instance = context.ApplicationInstance;
-                       if (app_instance != null)
-                               app_instance.TriggerPreSendRequestHeaders ();
-                               
-                       foreach (BaseResponseHeader header in write_headers){
-                               header.SendContent (WorkerRequest);
+                       if (context != null) {
+                               HttpApplication app_instance = context.ApplicationInstance;
+                               if (app_instance != null)
+                                       app_instance.TriggerPreSendRequestHeaders ();
+                       }
+                       if (WorkerRequest != null) {
+                               foreach (BaseResponseHeader header in write_headers){
+                                       header.SendContent (WorkerRequest);
+                               }
                        }
                        headers_sent = true;
                }
-               
+
+               internal void DoFilter (bool close)
+               {
+                       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 = null;
-
-                               WriteHeaders (final_flush);
+                                       use_chunked = false;
                        }
 
-                       if (suppress_content || context.Request.HttpMethod == "HEAD") {
+                       bool head = ((context != null) && (context.Request.HttpMethod == "HEAD"));
+                       if (suppress_content || head) {
+                               if (!headers_sent)
+                                       WriteHeaders (true);
                                output_stream.Clear ();
-                               output_stream.Flush (WorkerRequest, final_flush);
+                               if (WorkerRequest != null)
+                                       output_stream.Flush (WorkerRequest, true); // ignore final_flush here.
                                return;
                        }
 
-                       HttpApplication app_instance = context.ApplicationInstance;
-                       if (app_instance != null)
-                               app_instance.TriggerPreSendRequestContent ();
+                       if (!headers_sent)
+                               WriteHeaders (final_flush);
 
-                       if (IsCached) {
-                               byte [] data = output_stream.GetData ();
-                               cached_response.ContentLength = data.Length;
-                               cached_response.SetData (data);
+                       if (context != null) {
+                               HttpApplication app_instance = context.ApplicationInstance;
+                               if (app_instance != null)
+                                       app_instance.TriggerPreSendRequestContent ();
                        }
 
-                       output_stream.Flush (WorkerRequest, final_flush);
+                       if (IsCached) {
+                               MemoryStream ms = output_stream.GetData ();
+                               cached_response.ContentLength = (int) ms.Length;
+                               cached_response.SetData (ms.GetBuffer ());
+                       }
 
-                       // FIXME
-                       if (final_flush && use_chunked != null)
-                               Write ("0\r\n\r\n");
+                       if (WorkerRequest != null)
+                               output_stream.Flush (WorkerRequest, final_flush);
                }
 
                public void Flush ()
@@ -786,10 +833,15 @@ namespace System.Web {
                        if (buffer)
                                return;
 
+                       output_stream.ApplyFilter (false);
                        Flush ();
                }
 
-#if !TARGET_JVM
+#if TARGET_JVM
+               public void WriteFile (IntPtr fileHandle, long offset, long size) {
+                       throw new NotSupportedException("IntPtr not supported");
+               }
+#else
                public void WriteFile (IntPtr fileHandle, long offset, long size)
                {
                        if (offset < 0)
@@ -800,11 +852,14 @@ namespace System.Web {
                        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
@@ -827,9 +882,16 @@ namespace System.Web {
                        if (buffer)
                                return;
 
+                       output_stream.ApplyFilter (false);
                        Flush ();
                }
-
+#if NET_2_0
+               [MonoTODO]
+               public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
+               {
+                       throw new NotImplementedException ();
+               }
+#endif
                //
                // Like WriteFile, but never buffers, so we manually Flush here
                //
@@ -844,7 +906,9 @@ namespace System.Web {
                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);
                }
                
@@ -900,38 +964,38 @@ namespace System.Web {
                //
                public string CacheControl {
                        set {
-                               if (String.Compare (value, "public", true, CultureInfo.InvariantCulture) == 0 || 
-                                   String.Compare (value, "private", true, CultureInfo.InvariantCulture) == 0 ||
-                                   String.Compare (value, "no-cache", true, CultureInfo.InvariantCulture) == 0)
-                                       cache_control = value;
-                               else
+                               if (value == null || value == "") {
+                                       Cache.SetCacheability (HttpCacheability.NoCache);
+                                       user_cache_control = null;
+                               } else if (String.Compare (value, "public", true, CultureInfo.InvariantCulture) == 0) {
+                                       Cache.SetCacheability (HttpCacheability.Public);
+                                       user_cache_control = "public";
+                               } else if (String.Compare (value, "private", true, CultureInfo.InvariantCulture) == 0) {
+                                       Cache.SetCacheability (HttpCacheability.Private);
+                                       user_cache_control = "private";
+                               } else if (String.Compare (value, "no-cache", true, CultureInfo.InvariantCulture) == 0) {
+                                       Cache.SetCacheability (HttpCacheability.NoCache);
+                                       user_cache_control = "no-cache";
+                               } else
                                        throw new ArgumentException ("CacheControl property only allows `public', " +
                                                                     "`private' or no-cache, for different uses, use " +
                                                                     "Response.AppendHeader");
-
                        }
 
-                       get {
-                               if (cache_policy != null){
-                                       switch (Cache.Cacheability){
-                                       case HttpCacheability.NoCache: return "private";
-                                       case HttpCacheability.Private: return "private";
-                                       case HttpCacheability.Server: return "private";
-                                       case HttpCacheability.Public: return "public";
-                                       case HttpCacheability.ServerAndPrivate: return "private";
-                                       }
-                                       throw new Exception ("Unknown internal state: " + Cache.Cacheability);
-                               } else
-                                       return cache_control;
-                       }
+                       get { return (user_cache_control != null) ? user_cache_control : "private"; }
                }
 #endregion
 
+               internal int GetOutputByteCount ()
+               {
+                       return output_stream.GetTotalLength ();
+               }
+
                internal void ReleaseResources ()
                {
-                       output_stream.ReleaseResources ();
+                       output_stream.ReleaseResources (true);
                        output_stream = null;
                }
        }
-       
 }
+