X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2FSystem.Web%2FSystem.Web%2FHttpResponse.cs;h=9aa99d9244bba7f1fefdf3e5dd6a763be74ae5a0;hb=dff680e41a6368bee2f6a65395b7bfda59c09631;hp=fe972764d7110af89b421c3a81538ad48964b446;hpb=1c14f1ee6d701510c15499d71fc2b324151a8629;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..9aa99d9244b 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 @@ -36,17 +37,27 @@ 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 { - +#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 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 +74,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 +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 @@ -91,19 +105,18 @@ namespace System.Web { // Session State // string app_path_mod; - - // - // Passed as flags - // - internal object FlagEnd = new object (); + bool is_request_being_redirected; + Encoding headerEncoding; internal HttpResponse () { output_stream = new HttpResponseStream (this); + writer = new HttpWriter (this); } public HttpResponse (TextWriter writer) : this () { + this.writer = writer; } @@ -112,16 +125,53 @@ 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 + writer = new HttpWriter (this); } - + internal TextWriter SetTextWriter (TextWriter writer) { TextWriter prev = this.writer; this.writer = writer; return prev; } + + internal string VersionHeader { + get { + if (!version_header_checked && version_header == null) { + version_header_checked = true; + HttpRuntimeSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime") as HttpRuntimeSection; + 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) + 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 { @@ -244,17 +294,43 @@ namespace System.Web { output_stream.Filter = value; } } -#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; } } -#endif + + public NameValueCollection Headers { + get { + if (headers == null) + headers = new HttpHeaderCollection (); + + return headers; + } + } + + public bool IsClientConnected { get { if (WorkerRequest == null) @@ -263,19 +339,18 @@ namespace System.Web { return WorkerRequest.IsClientConnected (); } } -#if NET_2_0 - [MonoTODO ("Not implemented")] + public bool IsRequestBeingRedirected { - get { throw new NotImplementedException (); } + 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 { @@ -295,9 +370,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 (' '); @@ -306,22 +379,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; @@ -361,7 +438,7 @@ namespace System.Web { suppress_content = value; } } -#if NET_2_0 + [MonoTODO ("Not implemented")] public void AddCacheDependency (CacheDependency[] dependencies) { @@ -373,7 +450,7 @@ namespace System.Web { { throw new NotImplementedException (); } -#endif + [MonoTODO("Currently does nothing")] public void AddCacheItemDependencies (ArrayList cacheKeys) { @@ -385,23 +462,26 @@ 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")] + 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) @@ -422,31 +502,34 @@ namespace System.Web { public void AppendHeader (string name, string value) { if (headers_sent) - throw new HttpException ("headers have been already sent"); - - if (String.Compare (name, "content-length", true, CultureInfo.InvariantCulture) == 0){ + throw new HttpException ("Headers have been already sent"); +#if !TARGET_J2EE + if (String.Compare (name, "content-length", true, Helpers.InvariantCulture) == 0){ content_length = (long) UInt64.Parse (value); use_chunked = false; return; } +#endif - if (String.Compare (name, "content-type", true, CultureInfo.InvariantCulture) == 0){ + if (String.Compare (name, "content-type", true, Helpers.InvariantCulture) == 0){ ContentType = value; return; } - if (String.Compare (name, "transfer-encoding", true, CultureInfo.InvariantCulture) == 0){ +#if !TARGET_J2EE + if (String.Compare (name, "transfer-encoding", true, Helpers.InvariantCulture) == 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", true, Helpers.InvariantCulture) == 0){ user_cache_control = value; return; } - headers.Add (new UnknownResponseHeader (name, value)); + Headers.Add (name, value); } [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)] @@ -458,26 +541,29 @@ 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)) { virtualPath = UrlUtils.Combine (context.Request.RootVirtualDir, virtualPath); } else if (UrlUtils.IsRooted (virtualPath)) { virtualPath = UrlUtils.Canonic (virtualPath); } - + + bool cookieless = false; + SessionStateSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/sessionState") as SessionStateSection; + cookieless = SessionStateModule.IsCookieLess (context, config); + + 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 +587,7 @@ namespace System.Web { public void ClearContent () { output_stream.Clear (); + content_length = -1; } public void ClearHeaders () @@ -512,8 +599,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 +622,24 @@ namespace System.Web { closed = true; } + public void DisableKernelCache () + { + // does nothing in Mono + } + 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 +648,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 (Helpers.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 (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){ -#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 +698,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 +709,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 +725,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 +744,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) @@ -708,11 +826,8 @@ namespace System.Web { app_instance.TriggerPreSendRequestContent (); } - if (IsCached) { - MemoryStream ms = output_stream.GetData (); - cached_response.ContentLength = (int) ms.Length; - cached_response.SetData (ms.GetBuffer ()); - } + if (IsCached) + cached_response.SetData (output_stream.GetData ()); if (WorkerRequest != null) output_stream.Flush (WorkerRequest, final_flush); @@ -728,32 +843,156 @@ 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 ("header have been already sent"); + throw new HttpException ("Headers have already been sent"); + if (url.IndexOf ('\n') != -1) + throw new ArgumentException ("Redirect URI cannot contain newline characters.", "url"); + + is_request_being_redirected = true; ClearHeaders (); ClearContent (); - StatusCode = 302; + StatusCode = code; url = ApplyAppPathModifier (url); - headers.Add (new UnknownResponseHeader ("Location", 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 = WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime") as HttpRuntimeSection; + if (config != null && config.UseFullyQualifiedRedirectUrl) { + var ub = new UriBuilder (context.Request.Url); + ub.Path = url; + ub.Fragment = null; + ub.Password = null; + ub.Query = null; + ub.UserName = null; + url = ub.Uri.ToString (); + } + } + + 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 (); + is_request_being_redirected = false; + } + + 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) @@ -765,7 +1004,19 @@ namespace System.Web { if (path [0] != '/') throw new ArgumentException ("'" + path + "' is not an absolute virtual path."); - HttpRuntime.Cache.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) @@ -775,25 +1026,49 @@ namespace System.Web { public void Write (char ch) { - Output.Write (ch); + TextWriter writer = Output; +#if NET_4_0 + // Emulating .NET + if (writer == null) + throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required."); +#endif + writer.Write (ch); } public void Write (object obj) { + TextWriter writer = Output; +#if NET_4_0 + // Emulating .NET + if (writer == null) + throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required."); +#endif if (obj == null) return; - Output.Write (obj.ToString ()); + writer.Write (obj.ToString ()); } public void Write (string s) { - Output.Write (s); + TextWriter writer = Output; +#if NET_4_0 + // Emulating .NET + if (writer == null) + throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required."); +#endif + writer.Write (s); } public void Write (char [] buffer, int index, int count) { - Output.Write (buffer, index, count); + TextWriter writer = Output; +#if NET_4_0 + // Emulating .NET + if (writer == null) + throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required."); +#endif + writer.Write (buffer, index, count); } internal void WriteFile (FileStream fs, long offset, long size) @@ -837,7 +1112,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) @@ -883,13 +1158,35 @@ namespace System.Web { output_stream.ApplyFilter (false); Flush (); } -#if NET_2_0 - [MonoTODO ("Not implemented")] + public void WriteSubstitution (HttpResponseSubstitutionCallback callback) { - throw new NotImplementedException (); + // Emulation of .NET behavior + if (callback == null) + throw new NullReferenceException (); + + object target = callback.Target; + if (target != null && target.GetType () == typeof (Control)) + throw new ArgumentException ("callback"); + + string s = callback (context); + if (!IsCached) { + Write (s); + return; + } + + Cache.Cacheability = HttpCacheability.Server; + Flush (); + if (WorkerRequest == null) + Write (s); // better this than nothing + else { + byte[] bytes = WebEncoding.ResponseEncoding.GetBytes (s); + WorkerRequest.SendResponseFromMemory (bytes, bytes.Length); + } + + cached_response.SetData (callback); } -#endif + // // Like WriteFile, but never buffers, so we manually Flush here // @@ -909,8 +1206,43 @@ namespace System.Web { output_stream.ApplyFilter (final_flush); Flush (final_flush); } + + 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); + } + } + #region Session state support internal void SetAppPathModifier (string app_modifier) { @@ -919,40 +1251,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; } @@ -965,13 +1295,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 @@ -995,5 +1325,13 @@ namespace System.Web { output_stream = null; } } + +#if TARGET_J2EE + public +#endif + static class FlagEnd + { + public static readonly object Value = new object (); + } }