X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.Web%2FSystem.Web%2FHttpResponse.cs;h=053d20c9c58b9baa0a741ee35ea327dbf1a9ae48;hb=a0173a7e76ad48889ade46116e516731b170e7c5;hp=4ae2f77711d9ed4e8df57fa60544552fdbff03c2;hpb=f1f2c8e08f825b8c45c853061b9a881f600597ad;p=mono.git diff --git a/mcs/class/System.Web/System.Web/HttpResponse.cs b/mcs/class/System.Web/System.Web/HttpResponse.cs index 4ae2f77711d..053d20c9c58 100644 --- a/mcs/class/System.Web/System.Web/HttpResponse.cs +++ b/mcs/class/System.Web/System.Web/HttpResponse.cs @@ -5,8 +5,9 @@ // Author: // Miguel de Icaza (miguel@novell.com) // Gonzalo Paniagua Javier (gonzalo@ximian.com) +// Marek Habersack // -// 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; @@ -68,8 +74,8 @@ namespace System.Web { CachedRawResponse cached_response; string user_cache_control = "private"; string redirect_location; - - static UnknownResponseHeader version_header; + string version_header; + bool version_header_checked; // // Negative Content-Length means we auto-compute the size of content-length @@ -81,9 +87,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 (); + + HttpHeaderCollection headers; bool headers_sent; - ArrayList cached_headers; + NameValueCollection cached_headers; // // Transfer encoding state @@ -92,38 +99,25 @@ namespace System.Web { internal bool use_chunked; bool closed; + bool completed; internal bool suppress_content; // // Session State // string app_path_mod; - -#if NET_2_0 bool is_request_being_redirected; Encoding headerEncoding; -#endif - static HttpResponse () - { -#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) { - string version = Environment.Version.ToString (3); - version_header = new UnknownResponseHeader ("X-AspNet-Version", version); - } - } - internal HttpResponse () { output_stream = new HttpResponseStream (this); + writer = new HttpWriter (this); } public HttpResponse (TextWriter writer) : this () { + this.writer = writer; } @@ -133,11 +127,17 @@ namespace System.Web { this.context = context; #if !TARGET_J2EE - if (worker_request != null) - use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1"); + if (worker_request != null && worker_request.GetHttpVersion () == "HTTP/1.1") { + string gi = worker_request.GetServerVariable ("GATEWAY_INTERFACE"); + use_chunked = (String.IsNullOrEmpty (gi) || + !gi.StartsWith ("cgi", StringComparison.OrdinalIgnoreCase)); + } else { + use_chunked = false; + } #endif + writer = new HttpWriter (this); } - + internal TextWriter SetTextWriter (TextWriter writer) { TextWriter prev = this.writer; @@ -145,6 +145,24 @@ namespace System.Web { return prev; } + internal string VersionHeader { + get { + if (!version_header_checked && version_header == null) { + version_header_checked = true; + HttpRuntimeSection config = HttpRuntime.Section; + if (config != null && config.EnableVersionHeader) + version_header = Environment.Version.ToString (3); + } + + return version_header; + } + } + + internal HttpContext Context { + get { return context; } + set { context = value; } + } + internal string[] FileDependencies { get { if (fileDependencies == null || fileDependencies.Count == 0) @@ -282,7 +300,7 @@ namespace System.Web { output_stream.Filter = value; } } -#if NET_2_0 + public Encoding HeaderEncoding { get { if (headerEncoding == null) { @@ -309,14 +327,16 @@ namespace System.Web { } } - [MonoTODO ("Not implemented")] public NameValueCollection Headers { get { - // TODO: currently only return empty NameValueColletion - return new NameValueCollection (); + if (headers == null) + headers = new HttpHeaderCollection (); + + return headers; } } -#endif + + public bool IsClientConnected { get { if (WorkerRequest == null) @@ -325,18 +345,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 { @@ -365,22 +385,26 @@ 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); } } + // 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; + } + public int StatusCode { get { return status_code; @@ -421,7 +445,6 @@ namespace System.Web { } } -#if NET_2_0 [MonoTODO ("Not implemented")] public void AddCacheDependency (CacheDependency[] dependencies) { @@ -433,7 +456,7 @@ namespace System.Web { { throw new NotImplementedException (); } -#endif + [MonoTODO("Currently does nothing")] public void AddCacheItemDependencies (ArrayList cacheKeys) { @@ -452,14 +475,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) { @@ -486,35 +508,34 @@ 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, CultureInfo.InvariantCulture) == 0){ + if (String.Compare (name, "content-length", StringComparison.OrdinalIgnoreCase) == 0){ content_length = (long) UInt64.Parse (value); use_chunked = false; return; } #endif - if (String.Compare (name, "content-type", true, CultureInfo.InvariantCulture) == 0){ + if (String.Compare (name, "content-type", StringComparison.OrdinalIgnoreCase) == 0){ ContentType = value; return; } #if !TARGET_J2EE - if (String.Compare (name, "transfer-encoding", true, CultureInfo.InvariantCulture) == 0){ + if (String.Compare (name, "transfer-encoding", StringComparison.OrdinalIgnoreCase) == 0){ transfer_encoding = value; use_chunked = false; return; } #endif - if (String.Compare (name, "cache-control", true, CultureInfo.InvariantCulture) == 0){ + if (String.Compare (name, "cache-control", StringComparison.OrdinalIgnoreCase) == 0){ user_cache_control = value; return; } - headers.Add (new UnknownResponseHeader (name, value)); + Headers.Add (name, value); } [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)] @@ -526,10 +547,10 @@ namespace System.Web { public string ApplyAppPathModifier (string virtualPath) { - if (virtualPath == null) + if (virtualPath == null || context == null) return null; - if (virtualPath == "") + if (virtualPath.Length == 0) return context.Request.RootVirtualDir; if (UrlUtils.IsRelativeUrl (virtualPath)) { @@ -539,13 +560,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; @@ -588,8 +605,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 { @@ -607,12 +628,10 @@ namespace System.Web { closed = true; } -#if NET_2_0 public void DisableKernelCache () { // does nothing in Mono } -#endif public void End () { @@ -636,30 +655,31 @@ namespace System.Web { // Transfer-Encoding (chunked) // Cache-Control // X-AspNet-Version - void AddHeadersNoCache (ArrayList write_headers, bool final_flush) + 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 - if (version_header != null) - write_headers.Add (version_header); + 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 (Helpers.InvariantCulture)); } else if (BufferOutput) { if (final_flush) { // @@ -667,15 +687,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 (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 (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close")); + write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close"); } } } else { @@ -684,7 +704,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 @@ -695,7 +715,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 @@ -711,13 +731,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); @@ -746,18 +766,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); + } } } } @@ -770,6 +807,9 @@ namespace System.Web { internal void Flush (bool final_flush) { + if (completed) + throw new HttpException ("Server cannot flush a completed response"); + DoFilter (final_flush); if (!headers_sent){ if (final_flush || status_code != 200) @@ -783,9 +823,11 @@ namespace System.Web { output_stream.Clear (); if (WorkerRequest != null) output_stream.Flush (WorkerRequest, true); // ignore final_flush here. + completed = true; return; } - + completed = final_flush; + if (!headers_sent) WriteHeaders (final_flush); @@ -795,11 +837,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); @@ -815,24 +854,52 @@ 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); + + 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 = HttpRuntime.Section; + if (config != null && config.UseFullyQualifiedRedirectUrl) { + var ub = new UriBuilder (context.Request.Url); + int qpos = url.IndexOf ('?'); + if (qpos == -1) { + ub.Path = url; + ub.Query = null; + } else { + ub.Path = url.Substring (0, qpos); + ub.Query = url.Substring (qpos + 1); + } + ub.Fragment = null; + ub.Password = null; + ub.UserName = null; + url = ub.Uri.ToString (); + } + } + redirect_location = url; // Text for browsers that can't handle location header @@ -842,11 +909,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) @@ -858,7 +1021,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) @@ -868,27 +1043,82 @@ 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); } + bool IsFileSystemDirSeparator (char ch) + { + return ch == '\\' || ch == '/'; + } + + string GetNormalizedFileName (string fn) + { + if (String.IsNullOrEmpty (fn)) + return fn; + + // On Linux we don't change \ to / since filenames with \ are valid. We also + // don't remove drive: designator for the same reason. + int len = fn.Length; + if (len >= 3 && fn [1] == ':' && IsFileSystemDirSeparator (fn [2])) + return Path.GetFullPath (fn); // drive-qualified absolute file path + + bool startsWithDirSeparator = IsFileSystemDirSeparator (fn [0]); + if (len >= 2 && startsWithDirSeparator && IsFileSystemDirSeparator (fn [1])) + return Path.GetFullPath (fn); // UNC path + + if (!startsWithDirSeparator) { + HttpContext ctx = context ?? HttpContext.Current; + HttpRequest req = ctx != null ? ctx.Request : null; + + if (req != null) + return req.MapPath (fn); + } + + return fn; // Or should we rather throw? + } + internal void WriteFile (FileStream fs, long offset, long size) { byte [] buffer = new byte [32*1024]; @@ -914,12 +1144,13 @@ namespace System.Web { if (filename == null) throw new ArgumentNullException ("filename"); + string fn = GetNormalizedFileName (filename); if (readIntoMemory){ - using (FileStream fs = File.OpenRead (filename)) + using (FileStream fs = File.OpenRead (fn)) WriteFile (fs, 0, fs.Length); } else { - FileInfo fi = new FileInfo (filename); - output_stream.WriteFile (filename, 0, fi.Length); + FileInfo fi = new FileInfo (fn); + output_stream.WriteFile (fn, 0, fi.Length); } if (buffer) return; @@ -976,13 +1207,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 // @@ -1003,7 +1256,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); @@ -1039,7 +1291,6 @@ namespace System.Web { Flush (true); } } -#endif #region Session state support internal void SetAppPathModifier (string app_modifier) @@ -1049,9 +1300,10 @@ namespace System.Web { #endregion #region Cache Support - internal void SetCachedHeaders (ArrayList headers) + internal void SetCachedHeaders (NameValueCollection headers) { cached_headers = headers; + } internal bool IsCached { @@ -1092,13 +1344,13 @@ namespace System.Web { if (value == null || value == "") { Cache.SetCacheability (HttpCacheability.NoCache); user_cache_control = null; - } else if (String.Compare (value, "public", true, CultureInfo.InvariantCulture) == 0) { + } 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, CultureInfo.InvariantCulture) == 0) { + } 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, CultureInfo.InvariantCulture) == 0) { + } else if (String.Compare (value, "no-cache", true, Helpers.InvariantCulture) == 0) { Cache.SetCacheability (HttpCacheability.NoCache); user_cache_control = "no-cache"; } else @@ -1118,8 +1370,13 @@ namespace System.Web { internal void ReleaseResources () { - output_stream.ReleaseResources (true); - output_stream = null; + if (output_stream != null) + output_stream.ReleaseResources (true); + if (completed) + return; + + Close (); + completed = true; } }