2009-06-04 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web / HttpResponse.cs
index 8720b459d7f775b1d9a94bb66c950a82806efa53..7ed591b59ee7c4bd7f11d7f83c6a41ab26fa822c 100644 (file)
@@ -40,6 +40,7 @@ using System.Web.Configuration;
 using System.Globalization;
 using System.Security.Permissions;
 using System.Web.Hosting;
+using System.Web.SessionState;
 
 namespace System.Web {
        
@@ -67,6 +68,8 @@ namespace System.Web {
                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
@@ -78,9 +81,10 @@ namespace System.Web {
                // The list of the headers that we will send back to the client, except
                // the headers that we compute here.
                //
-               ArrayList headers = new ArrayList ();
+
+               NameValueCollection headers;
                bool headers_sent;
-               ArrayList cached_headers;
+               NameValueCollection cached_headers;
 
                //
                // Transfer encoding state
@@ -100,7 +104,7 @@ namespace System.Web {
                bool is_request_being_redirected;
                Encoding headerEncoding;
 #endif
-               
+
                internal HttpResponse ()
                {
                        output_stream = new HttpResponseStream (this);
@@ -129,6 +133,23 @@ namespace System.Web {
                        return prev;
                }
 
+               internal string VersionHeader {
+                       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);
+                               }
+
+                               return version_header;
+                       }
+               }
+               
                internal string[] FileDependencies {
                        get {
                                if (fileDependencies == null || fileDependencies.Count == 0)
@@ -292,7 +313,21 @@ namespace System.Web {
                                headerEncoding = value;
                        }
                }
+
+               public
+#else
+               internal
 #endif
+               NameValueCollection Headers {
+                       get {
+                               if (headers == null)
+                                       headers = new NameValueCollection ();
+
+                               return headers;
+                       }
+               }
+
+               
                public bool IsClientConnected {
                        get {
                                if (WorkerRequest == null)
@@ -357,6 +392,21 @@ namespace System.Web {
                        }
                }
 
+#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.
+               public int SubStatusCode {
+                       get;
+                       set;
+               }
+
+               public bool TrySkipIisCustomErrors {
+                       get;
+                       set;
+               }
+#endif
+               
                public int StatusCode {
                        get {
                                return status_code;
@@ -490,7 +540,7 @@ namespace System.Web {
                                return;
                        }
 
-                       headers.Add (new UnknownResponseHeader (name, value));
+                       Headers.Add (name, value);
                }
 
                [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
@@ -505,23 +555,30 @@ namespace System.Web {
                        if (virtualPath == null)
                                return null;
                
-                       if (virtualPath == "")
+                       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;
+#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;
+
                        if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) < 0) {
-                               string rvd = context.Request.RootVirtualDir;
-                               string basevd = rvd.Replace (app_path_mod, "");
-               
-                               if (!StrUtils.StartsWith (virtualPath, basevd))
-                                       return virtualPath;
-               
-                               virtualPath = UrlUtils.Combine (rvd, virtualPath.Substring (basevd.Length));
+                               if (UrlUtils.HasSessionId (virtualPath))
+                                       virtualPath = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (virtualPath), virtualPath);
+                               return UrlUtils.InsertSessionId (app_path_mod, virtualPath);
                        }
                
                        return virtualPath;
@@ -557,8 +614,12 @@ namespace System.Web {
                        content_length = -1;
                        content_type = "text/html";
                        transfer_encoding = null;
-                       user_cache_control = null;
-                       headers.Clear ();
+                       user_cache_control = "private";
+                       if (cache_policy != null)
+                               cache_policy.Cacheability = HttpCacheability.Private;
+
+                       if (headers != null)
+                               headers.Clear ();
                }
 
                internal bool HeadersSent {
@@ -576,6 +637,13 @@ namespace System.Web {
                        closed = true;
                }
 
+#if NET_2_0
+               public void DisableKernelCache ()
+               {
+                       // does nothing in Mono
+               }
+#endif
+               
                public void End ()
                {
                        if (context == null)
@@ -597,27 +665,32 @@ namespace System.Web {
                //   Content-Type
                //   Transfer-Encoding (chunked)
                //   Cache-Control
-               void AddHeadersNoCache (ArrayList write_headers, bool final_flush)
+               //   X-AspNet-Version
+               void AddHeadersNoCache (NameValueCollection write_headers, bool final_flush)
                {
 #if !TARGET_J2EE
                        //
                        // Transfer-Encoding
                        //
                        if (use_chunked)
-                               write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", "chunked"));
+                               write_headers.Add ("Transfer-Encoding", "chunked");
                        else if (transfer_encoding != null)
-                               write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", transfer_encoding));
+                               write_headers.Add ("Transfer-Encoding", transfer_encoding);
 #endif
                        if (redirect_location != null)
-                               write_headers.Add (new UnknownResponseHeader ("Location", redirect_location));
+                               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 (new KnownResponseHeader (HttpWorkerRequest.HeaderContentLength,
-                                                                     content_length.ToString (CultureInfo.InvariantCulture)));
+                               write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
+                                                  content_length.ToString (CultureInfo.InvariantCulture));
                        } else if (BufferOutput) {
                                if (final_flush) {                                      
                                        //
@@ -625,15 +698,15 @@ namespace System.Web {
                                        // we know the content-length.
                                        //
                                        content_length = output_stream.total;
-                                       write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderContentLength,
-                                                                             content_length.ToString (CultureInfo.InvariantCulture)));
+                                       write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
+                                                          content_length.ToString (CultureInfo.InvariantCulture));
                                } else {
                                        //
                                        // We are buffering, and this is a flush in the middle.
                                        // If we are not chunked, we need to set "Connection: close".
                                        //
                                        if (use_chunked){
-                                               write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
+                                               write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
                                        }
                                }
                        } else {
@@ -642,7 +715,7 @@ namespace System.Web {
                                // close at the end.
                                //
                                if (use_chunked){
-                                       write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
+                                       write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
                                }
                        }
 #endif
@@ -653,7 +726,7 @@ namespace System.Web {
                        if (cache_policy != null)
                                cache_policy.SetHeaders (this, headers);
                        else
-                               write_headers.Add (new UnknownResponseHeader ("Cache-Control", CacheControl));
+                               write_headers.Add ("Cache-Control", CacheControl);
                        
                        //
                        // Content-Type
@@ -669,13 +742,13 @@ namespace System.Web {
                                        }
                                }
                                
-                               write_headers.Add (new UnknownResponseHeader ("Content-Type", header));
+                               write_headers.Add ("Content-Type", header);
                        }
 
                        if (cookies != null && cookies.Count != 0){
                                int n = cookies.Count;
                                for (int i = 0; i < n; i++)
-                                       write_headers.Add (cookies.Get (i).GetCookieHeader ());
+                                       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);
@@ -704,18 +777,35 @@ namespace System.Web {
 
                        // If this page is cached use the cached headers
                        // instead of the standard headers      
-                       ArrayList write_headers = headers;
+                       NameValueCollection write_headers;
                        if (cached_headers != null)
                                write_headers = cached_headers;
-                       else
+                       else {
+                               write_headers = Headers;
                                AddHeadersNoCache (write_headers, final_flush);
-
+                       }
+                       
                        if (WorkerRequest != null)
                                WorkerRequest.SendStatus (status_code, StatusDescription);
 
                        if (WorkerRequest != null) {
-                               foreach (BaseResponseHeader header in write_headers){
-                                       header.SendContent (WorkerRequest);
+                               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);
+                                       }
                                }
                        }
                }
@@ -795,7 +885,7 @@ namespace System.Web {
 
                        // 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)
@@ -962,23 +1052,39 @@ namespace System.Web {
                }
 
 #if NET_2_0
+               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 ()) {
-                               long len = s.Length;
-                               byte[] buf = new byte [len];
-                               int readBytes = s.Read (buf, 0, (int) len);
-                               output_stream.Write (buf, 0, readBytes);
-                               output_stream.ApplyFilter (final_flush);
-                               Flush (final_flush);
+                               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);
                        }
                }
 #endif
@@ -991,40 +1097,38 @@ namespace System.Web {
 #endregion
                
 #region Cache Support
-               internal void SetCachedHeaders (ArrayList headers)
+               internal void SetCachedHeaders (NameValueCollection headers)
                {
                        cached_headers = headers;
+                       
                }
 
                internal bool IsCached {
-                       get {
-                               return cached_response != null;
+                       get { return cached_response != null; }
+                       set {
+                               if (value)
+                                       cached_response = new CachedRawResponse (cache_policy);
+                               else
+                                       cached_response = null;
                        }
                }
 
                public HttpCachePolicy Cache {
                        get {
-                               if (cache_policy == null) {
+                               if (cache_policy == null)
                                        cache_policy = new HttpCachePolicy ();
-                                       cache_policy.CacheabilityUpdated += new CacheabilityUpdatedCallback (OnCacheabilityUpdated);
-                               }
                                
                                return cache_policy;
                        }
-               }
-               
-               private void OnCacheabilityUpdated (object sender, CacheabilityUpdatedEventArgs e)
-               {
-                       if (e.Cacheability >= HttpCacheability.Server && !IsCached)
-                               cached_response = new CachedRawResponse (cache_policy);
-                       else if (e.Cacheability <= HttpCacheability.Private)
-                               cached_response = null;
-               }
+               }               
 
                internal CachedRawResponse GetCachedResponse ()
                {
-                       cached_response.StatusCode = StatusCode;
-                       cached_response.StatusDescription = StatusDescription;
+                       if (cached_response != null) {
+                               cached_response.StatusCode = StatusCode;
+                               cached_response.StatusDescription = StatusDescription;
+                       }
+                       
                        return cached_response;
                }