X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.Web%2FSystem.Web%2FHttpResponse.cs;h=7ed591b59ee7c4bd7f11d7f83c6a41ab26fa822c;hb=60f847468e0e5740947aea5e0df987bc6b052e77;hp=fe972764d7110af89b421c3a81538ad48964b446;hpb=881f83658281916d8f0784df7c726ecb7cc289db;p=mono.git diff --git a/mcs/class/System.Web/System.Web/HttpResponse.cs b/mcs/class/System.Web/System.Web/HttpResponse.cs index fe972764d71..7ed591b59ee 100644 --- a/mcs/class/System.Web/System.Web/HttpResponse.cs +++ b/mcs/class/System.Web/System.Web/HttpResponse.cs @@ -36,17 +36,22 @@ using System.IO; using System.Web.Caching; using System.Threading; using System.Web.Util; +using System.Web.Configuration; using System.Globalization; using System.Security.Permissions; +using System.Web.Hosting; +using System.Web.SessionState; namespace System.Web { // CAS - no InheritanceDemand here as the class is sealed [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)] - public sealed class HttpResponse { + public sealed partial class HttpResponse { internal HttpWorkerRequest WorkerRequest; internal HttpResponseStream output_stream; internal bool buffer = true; + + ArrayList fileDependencies; HttpContext context; TextWriter writer; @@ -63,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 @@ -74,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 @@ -92,10 +100,10 @@ namespace System.Web { // string app_path_mod; - // - // Passed as flags - // - internal object FlagEnd = new object (); +#if NET_2_0 + bool is_request_being_redirected; + Encoding headerEncoding; +#endif internal HttpResponse () { @@ -112,8 +120,10 @@ namespace System.Web { WorkerRequest = worker_request; this.context = context; +#if !TARGET_J2EE if (worker_request != null) use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1"); +#endif } internal TextWriter SetTextWriter (TextWriter writer) @@ -122,6 +132,39 @@ namespace System.Web { this.writer = writer; 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) + return new string[0] {}; + return (string[]) fileDependencies.ToArray (typeof (string)); + } + } + + ArrayList FileDependenciesArray { + get { + if (fileDependencies == null) + fileDependencies = new ArrayList (); + return fileDependencies; + } + } public bool Buffer { get { @@ -245,16 +288,46 @@ namespace System.Web { } } #if NET_2_0 - [MonoTODO ("Not implemented")] public Encoding HeaderEncoding { - get { throw new NotImplementedException (); } + get { + 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 (headers_sent) + throw new HttpException ("headers have already been sent"); if (value == null) throw new ArgumentNullException ("HeaderEncoding"); - throw new NotImplementedException (); + if (value == Encoding.Unicode) + throw new HttpException ("HeaderEncoding must not be Unicode"); + headerEncoding = value; } } + + public +#else + internal #endif + NameValueCollection Headers { + get { + if (headers == null) + headers = new NameValueCollection (); + + return headers; + } + } + + public bool IsClientConnected { get { if (WorkerRequest == null) @@ -264,9 +337,8 @@ namespace System.Web { } } #if NET_2_0 - [MonoTODO ("Not implemented")] public bool IsRequestBeingRedirected { - get { throw new NotImplementedException (); } + get { return is_request_being_redirected; } } #endif public TextWriter Output { @@ -295,9 +367,7 @@ namespace System.Web { } public string Status { - get { - return String.Format ("{0} {1}", status_code, StatusDescription); - } + get { return String.Concat (status_code.ToString (), " ", StatusDescription); } set { int p = value.IndexOf (' '); @@ -322,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; @@ -361,6 +446,7 @@ namespace System.Web { suppress_content = value; } } + #if NET_2_0 [MonoTODO ("Not implemented")] public void AddCacheDependency (CacheDependency[] dependencies) @@ -385,23 +471,27 @@ namespace System.Web { { // TODO: talk to jackson about the cache } - - [MonoTODO("Currently does nothing")] + public void AddFileDependencies (ArrayList filenames) { - // TODO: talk to jackson about the cache + if (filenames == null || filenames.Count == 0) + return; + FileDependenciesArray.AddRange (filenames); } #if NET_2_0 - [MonoTODO ("Not implemented")] public void AddFileDependencies (string[] filenames) { - throw new NotImplementedException (); + if (filenames == null || filenames.Length == 0) + return; + FileDependenciesArray.AddRange (filenames); } -#endif - [MonoTODO ("Currently does nothing")] +#endif + public void AddFileDependency (string filename) { - // TODO: talk to jackson about the cache + if (filename == null || filename == String.Empty) + return; + FileDependenciesArray.Add (filename); } public void AddHeader (string name, string value) @@ -424,29 +514,33 @@ namespace System.Web { if (headers_sent) throw new HttpException ("headers have been already sent"); +#if !TARGET_J2EE if (String.Compare (name, "content-length", true, CultureInfo.InvariantCulture) == 0){ content_length = (long) UInt64.Parse (value); use_chunked = false; return; } +#endif if (String.Compare (name, "content-type", true, CultureInfo.InvariantCulture) == 0){ ContentType = value; return; } +#if !TARGET_J2EE if (String.Compare (name, "transfer-encoding", true, CultureInfo.InvariantCulture) == 0){ transfer_encoding = value; use_chunked = false; return; } +#endif if (String.Compare (name, "cache-control", true, CultureInfo.InvariantCulture) == 0){ user_cache_control = value; return; } - headers.Add (new UnknownResponseHeader (name, value)); + Headers.Add (name, value); } [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)] @@ -461,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; @@ -501,6 +602,7 @@ namespace System.Web { public void ClearContent () { output_stream.Clear (); + content_length = -1; } public void ClearHeaders () @@ -512,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 { @@ -531,14 +637,26 @@ namespace System.Web { closed = true; } +#if NET_2_0 + public void DisableKernelCache () + { + // does nothing in Mono + } +#endif + public void End () { + if (context == null) + return; + if (context.TimeoutPossible) { - Thread.CurrentThread.Abort (FlagEnd); + Thread.CurrentThread.Abort (FlagEnd.Value); } else { // If this is called from an async event, signal the completion // but don't throw. - context.ApplicationInstance.CompleteRequest (); + HttpApplication app_instance = context.ApplicationInstance; + if (app_instance != null) + app_instance.CompleteRequest (); } } @@ -547,51 +665,48 @@ 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)); - - UnknownResponseHeader date_header = new UnknownResponseHeader ("Date", - DateTime.UtcNow.ToString ("r", CultureInfo.InvariantCulture)); - write_headers.Add (date_header); - - if (IsCached) - cached_response.DateHeader = date_header; - + 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))); - } else if (Buffer){ - if (final_flush){ + if (content_length >= 0) { + write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength), + content_length.ToString (CultureInfo.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 (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){ -#if DEBUG - Console.WriteLine ("Setting to close2"); -#endif - write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close")); + write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close"); } } } else { @@ -600,12 +715,10 @@ namespace System.Web { // close at the end. // if (use_chunked){ -#if DEBUG - Console.WriteLine ("Setting to close"); -#endif - write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close")); + write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close"); } } +#endif // // Cache Control, the cache policy takes precedence over the cache_control property. @@ -613,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 @@ -629,15 +742,18 @@ 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); +#endif } - } internal void WriteHeaders (bool final_flush) @@ -645,34 +761,53 @@ namespace System.Web { if (headers_sent) return; - if (WorkerRequest != null) - WorkerRequest.SendStatus (status_code, StatusDescription); + // + // Flush + // + if (context != null) { + HttpApplication app_instance = context.ApplicationInstance; + if (app_instance != null) + app_instance.TriggerPreSendRequestHeaders (); + } + + headers_sent = true; 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; + NameValueCollection write_headers; if (cached_headers != null) write_headers = cached_headers; - else + else { + write_headers = Headers; AddHeadersNoCache (write_headers, final_flush); - - // - // Flush - // - if (context != null) { - HttpApplication app_instance = context.ApplicationInstance; - if (app_instance != null) - app_instance.TriggerPreSendRequestHeaders (); } + + 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); + } } } - headers_sent = true; } internal void DoFilter (bool close) @@ -736,22 +871,28 @@ namespace System.Web { public void Redirect (string url, bool endResponse) { if (headers_sent) - throw new HttpException ("header have been already sent"); + throw new HttpException ("Headers have already been sent"); +#if NET_2_0 + is_request_being_redirected = true; +#endif ClearHeaders (); ClearContent (); StatusCode = 302; url = ApplyAppPathModifier (url); - headers.Add (new UnknownResponseHeader ("Location", url)); + redirect_location = url; // Text for browsers that can't handle location header Write ("Object moved\r\n"); - Write ("

Object moved to here

\r\n"); + Write ("

Object moved to here

\r\n"); Write ("\r\n"); if (endResponse) End (); +#if NET_2_0 + is_request_being_redirected = false; +#endif } public static void RemoveOutputCacheItem (string path) @@ -765,7 +906,7 @@ namespace System.Web { if (path [0] != '/') throw new ArgumentException ("'" + path + "' is not an absolute virtual path."); - HttpRuntime.Cache.Remove (path); + HttpRuntime.InternalCache.Remove (path); } public void SetCookie (HttpCookie cookie) @@ -837,7 +978,7 @@ namespace System.Web { #if TARGET_JVM public void WriteFile (IntPtr fileHandle, long offset, long size) { - throw new NotSupportedException("IntPtr not supported"); + throw new PlatformNotSupportedException("IntPtr not supported"); } #else public void WriteFile (IntPtr fileHandle, long offset, long size) @@ -909,8 +1050,45 @@ namespace System.Web { output_stream.ApplyFilter (final_flush); Flush (final_flush); } + +#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 ()) { + 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 + #region Session state support internal void SetAppPathModifier (string app_modifier) { @@ -919,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; } @@ -995,5 +1171,13 @@ namespace System.Web { output_stream = null; } } + +#if TARGET_J2EE + public +#endif + static class FlagEnd + { + public static readonly object Value = new object (); + } }