2 // System.Web.HttpResponse.cs
6 // Miguel de Icaza (miguel@novell.com)
7 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 // Marek Habersack <mhabersack@novell.com>
10 // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections;
35 using System.Collections.Specialized;
37 using System.Web.Caching;
38 using System.Threading;
39 using System.Web.Util;
40 using System.Web.Configuration;
41 using System.Globalization;
42 using System.Security.Permissions;
43 using System.Web.Hosting;
44 using System.Web.SessionState;
47 using System.Web.Routing;
52 // CAS - no InheritanceDemand here as the class is sealed
53 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
54 public sealed partial class HttpResponse
56 internal HttpWorkerRequest WorkerRequest;
57 internal HttpResponseStream output_stream;
58 internal bool buffer = true;
60 ArrayList fileDependencies;
64 HttpCachePolicy cache_policy;
66 HttpCookieCollection cookies;
68 int status_code = 200;
69 string status_description = "OK";
71 string content_type = "text/html";
74 CachedRawResponse cached_response;
75 string user_cache_control = "private";
76 string redirect_location;
77 string version_header;
78 bool version_header_checked;
81 // Negative Content-Length means we auto-compute the size of content-length
82 // can be overwritten with AppendHeader ("Content-Length", value)
84 long content_length = -1;
87 // The list of the headers that we will send back to the client, except
88 // the headers that we compute here.
91 HttpHeaderCollection headers;
93 NameValueCollection cached_headers;
96 // Transfer encoding state
98 string transfer_encoding;
99 internal bool use_chunked;
102 internal bool suppress_content;
108 bool is_request_being_redirected;
109 Encoding headerEncoding;
111 internal HttpResponse ()
113 output_stream = new HttpResponseStream (this);
114 writer = new HttpWriter (this);
117 public HttpResponse (TextWriter writer) : this ()
120 this.writer = writer;
123 internal HttpResponse (HttpWorkerRequest worker_request, HttpContext context) : this ()
125 WorkerRequest = worker_request;
126 this.context = context;
129 if (worker_request != null)
130 use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1");
132 writer = new HttpWriter (this);
135 internal TextWriter SetTextWriter (TextWriter writer)
137 TextWriter prev = this.writer;
138 this.writer = writer;
142 internal string VersionHeader {
144 if (!version_header_checked && version_header == null) {
145 version_header_checked = true;
146 HttpRuntimeSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime") as HttpRuntimeSection;
147 if (config != null && config.EnableVersionHeader)
148 version_header = Environment.Version.ToString (3);
151 return version_header;
155 internal HttpContext Context {
156 get { return context; }
157 set { context = value; }
160 internal string[] FileDependencies {
162 if (fileDependencies == null || fileDependencies.Count == 0)
163 return new string[0] {};
164 return (string[]) fileDependencies.ToArray (typeof (string));
168 ArrayList FileDependenciesArray {
170 if (fileDependencies == null)
171 fileDependencies = new ArrayList ();
172 return fileDependencies;
186 public bool BufferOutput {
197 // Use the default from <globalization> section if the client has not set the encoding
199 public Encoding ContentEncoding {
201 if (encoding == null) {
202 if (context != null) {
203 string client_content_type = context.Request.ContentType;
204 string parameter = HttpRequest.GetParameter (client_content_type, "; charset=");
205 if (parameter != null) {
207 // Do what the #1 web server does
208 encoding = Encoding.GetEncoding (parameter);
213 if (encoding == null)
214 encoding = WebEncoding.ResponseEncoding;
221 throw new ArgumentException ("ContentEncoding can not be null");
224 HttpWriter http_writer = writer as HttpWriter;
225 if (http_writer != null)
226 http_writer.SetEncoding (encoding);
230 public string ContentType {
236 content_type = value;
240 public string Charset {
243 charset = ContentEncoding.WebName;
254 public HttpCookieCollection Cookies {
257 cookies = new HttpCookieCollection (true, false);
264 if (cache_policy == null)
267 return cache_policy.ExpireMinutes ();
271 Cache.SetExpires (DateTime.Now + new TimeSpan (0, value, 0));
275 public DateTime ExpiresAbsolute {
277 return Cache.Expires;
281 Cache.SetExpires (value);
285 public Stream Filter {
287 if (WorkerRequest == null)
290 return output_stream.Filter;
294 output_stream.Filter = value;
298 public Encoding HeaderEncoding {
300 if (headerEncoding == null) {
301 GlobalizationSection gs = WebConfigurationManager.SafeGetSection ("system.web/globalization", typeof (GlobalizationSection)) as GlobalizationSection;
304 headerEncoding = Encoding.UTF8;
306 headerEncoding = gs.ResponseHeaderEncoding;
307 if (headerEncoding == Encoding.Unicode)
308 throw new HttpException ("HeaderEncoding must not be Unicode");
311 return headerEncoding;
315 throw new HttpException ("headers have already been sent");
317 throw new ArgumentNullException ("HeaderEncoding");
318 if (value == Encoding.Unicode)
319 throw new HttpException ("HeaderEncoding must not be Unicode");
320 headerEncoding = value;
324 public NameValueCollection Headers {
327 headers = new HttpHeaderCollection ();
334 public bool IsClientConnected {
336 if (WorkerRequest == null)
337 return true; // yep that's true
339 return WorkerRequest.IsClientConnected ();
343 public bool IsRequestBeingRedirected {
344 get { return is_request_being_redirected; }
347 public TextWriter Output {
352 set { writer = value; }
356 public Stream OutputStream {
358 return output_stream;
362 public string RedirectLocation {
364 return redirect_location;
368 redirect_location = value;
372 public string Status {
373 get { return String.Concat (status_code.ToString (), " ", StatusDescription); }
376 int p = value.IndexOf (' ');
378 throw new HttpException ("Invalid format for the Status property");
380 string s = value.Substring (0, p);
382 if (!Int32.TryParse (s, out status_code))
383 throw new HttpException ("Invalid format for the Status property");
385 status_description = value.Substring (p+1);
389 // We ignore the two properties on Mono as they are for use with IIS7, but there is
390 // no point in throwing PlatformNotSupportedException. We might find a use for them
392 public int SubStatusCode {
397 public bool TrySkipIisCustomErrors {
402 public int StatusCode {
409 throw new HttpException ("headers have already been sent");
412 status_description = null;
416 public string StatusDescription {
418 if (status_description == null)
419 status_description = HttpWorkerRequest.GetStatusDescription (status_code);
421 return status_description;
426 throw new HttpException ("headers have already been sent");
428 status_description = value;
432 public bool SuppressContent {
434 return suppress_content;
438 suppress_content = value;
442 [MonoTODO ("Not implemented")]
443 public void AddCacheDependency (CacheDependency[] dependencies)
445 throw new NotImplementedException ();
448 [MonoTODO ("Not implemented")]
449 public void AddCacheItemDependencies (string[] cacheKeys)
451 throw new NotImplementedException ();
454 [MonoTODO("Currently does nothing")]
455 public void AddCacheItemDependencies (ArrayList cacheKeys)
457 // TODO: talk to jackson about the cache
460 [MonoTODO("Currently does nothing")]
461 public void AddCacheItemDependency (string cacheKey)
463 // TODO: talk to jackson about the cache
466 public void AddFileDependencies (ArrayList filenames)
468 if (filenames == null || filenames.Count == 0)
470 FileDependenciesArray.AddRange (filenames);
473 public void AddFileDependencies (string[] filenames)
475 if (filenames == null || filenames.Length == 0)
477 FileDependenciesArray.AddRange (filenames);
480 public void AddFileDependency (string filename)
482 if (filename == null || filename == String.Empty)
484 FileDependenciesArray.Add (filename);
487 public void AddHeader (string name, string value)
489 AppendHeader (name, value);
492 public void AppendCookie (HttpCookie cookie)
494 Cookies.Add (cookie);
499 // Special case for Content-Length, Content-Type, Transfer-Encoding and Cache-Control
502 public void AppendHeader (string name, string value)
505 throw new HttpException ("Headers have been already sent");
507 if (String.Compare (name, "content-length", true, Helpers.InvariantCulture) == 0){
508 content_length = (long) UInt64.Parse (value);
514 if (String.Compare (name, "content-type", true, Helpers.InvariantCulture) == 0){
520 if (String.Compare (name, "transfer-encoding", true, Helpers.InvariantCulture) == 0){
521 transfer_encoding = value;
527 if (String.Compare (name, "cache-control", true, Helpers.InvariantCulture) == 0){
528 user_cache_control = value;
532 Headers.Add (name, value);
535 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
536 public void AppendToLog (string param)
538 Console.Write ("System.Web: ");
539 Console.WriteLine (param);
542 public string ApplyAppPathModifier (string virtualPath)
544 if (virtualPath == null || context == null)
547 if (virtualPath.Length == 0)
548 return context.Request.RootVirtualDir;
550 if (UrlUtils.IsRelativeUrl (virtualPath)) {
551 virtualPath = UrlUtils.Combine (context.Request.RootVirtualDir, virtualPath);
552 } else if (UrlUtils.IsRooted (virtualPath)) {
553 virtualPath = UrlUtils.Canonic (virtualPath);
556 bool cookieless = false;
557 SessionStateSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/sessionState") as SessionStateSection;
558 cookieless = SessionStateModule.IsCookieLess (context, config);
563 if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) < 0) {
564 if (UrlUtils.HasSessionId (virtualPath))
565 virtualPath = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (virtualPath), virtualPath);
566 return UrlUtils.InsertSessionId (app_path_mod, virtualPath);
572 public void BinaryWrite (byte [] buffer)
574 output_stream.Write (buffer, 0, buffer.Length);
577 internal void BinaryWrite (byte [] buffer, int start, int len)
579 output_stream.Write (buffer, start, len);
587 public void ClearContent ()
589 output_stream.Clear ();
593 public void ClearHeaders ()
596 throw new HttpException ("headers have been already sent");
598 // Reset the special case headers.
600 content_type = "text/html";
601 transfer_encoding = null;
602 user_cache_control = "private";
603 if (cache_policy != null)
604 cache_policy.Cacheability = HttpCacheability.Private;
610 internal bool HeadersSent {
620 if (WorkerRequest != null)
621 WorkerRequest.CloseConnection ();
625 public void DisableKernelCache ()
627 // does nothing in Mono
635 if (context.TimeoutPossible) {
636 Thread.CurrentThread.Abort (FlagEnd.Value);
638 // If this is called from an async event, signal the completion
640 HttpApplication app_instance = context.ApplicationInstance;
641 if (app_instance != null)
642 app_instance.CompleteRequest ();
649 // Transfer-Encoding (chunked)
652 void AddHeadersNoCache (NameValueCollection write_headers, bool final_flush)
659 write_headers.Add ("Transfer-Encoding", "chunked");
660 else if (transfer_encoding != null)
661 write_headers.Add ("Transfer-Encoding", transfer_encoding);
663 if (redirect_location != null)
664 write_headers.Add ("Location", redirect_location);
667 string vh = VersionHeader;
669 write_headers.Add ("X-AspNet-Version", vh);
672 // If Content-Length is set.
674 if (content_length >= 0) {
675 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
676 content_length.ToString (Helpers.InvariantCulture));
677 } else if (BufferOutput) {
680 // If we are buffering and this is the last flush, not a middle-flush,
681 // we know the content-length.
683 content_length = output_stream.total;
684 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
685 content_length.ToString (Helpers.InvariantCulture));
688 // We are buffering, and this is a flush in the middle.
689 // If we are not chunked, we need to set "Connection: close".
692 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
697 // If the content-length is not set, and we are not buffering, we must
701 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
707 // Cache Control, the cache policy takes precedence over the cache_control property.
709 if (cache_policy != null)
710 cache_policy.SetHeaders (this, headers);
712 write_headers.Add ("Cache-Control", CacheControl);
717 if (content_type != null){
718 string header = content_type;
720 if (charset_set || header == "text/plain" || header == "text/html") {
721 if (header.IndexOf ("charset=") == -1) {
722 if (charset == null || charset == "")
723 charset = ContentEncoding.HeaderName;
724 header += "; charset=" + charset;
728 write_headers.Add ("Content-Type", header);
731 if (cookies != null && cookies.Count != 0){
732 int n = cookies.Count;
733 for (int i = 0; i < n; i++)
734 write_headers.Add ("Set-Cookie", cookies.Get (i).GetCookieHeaderValue ());
736 // For J2EE Portal support emulate cookies by storing them in the session.
737 context.Request.SetSessionCookiesForPortal (cookies);
742 internal void WriteHeaders (bool final_flush)
750 if (context != null) {
751 HttpApplication app_instance = context.ApplicationInstance;
752 if (app_instance != null)
753 app_instance.TriggerPreSendRequestHeaders ();
758 if (cached_response != null)
759 cached_response.SetHeaders (headers);
761 // If this page is cached use the cached headers
762 // instead of the standard headers
763 NameValueCollection write_headers;
764 if (cached_headers != null)
765 write_headers = cached_headers;
767 write_headers = Headers;
768 AddHeadersNoCache (write_headers, final_flush);
771 if (WorkerRequest != null)
772 WorkerRequest.SendStatus (status_code, StatusDescription);
774 if (WorkerRequest != null) {
779 for (int i = 0; i < write_headers.Count; i++) {
780 header_name = write_headers.GetKey (i);
781 header_index = HttpWorkerRequest.GetKnownResponseHeaderIndex (header_name);
782 values = write_headers.GetValues (i);
786 foreach (string val in values) {
787 if (header_index > -1)
788 WorkerRequest.SendKnownResponseHeader (header_index, val);
790 WorkerRequest.SendUnknownResponseHeader (header_name, val);
796 internal void DoFilter (bool close)
798 if (output_stream.HaveFilter && context != null && context.Error == null)
799 output_stream.ApplyFilter (close);
802 internal void Flush (bool final_flush)
804 DoFilter (final_flush);
806 if (final_flush || status_code != 200)
810 bool head = ((context != null) && (context.Request.HttpMethod == "HEAD"));
811 if (suppress_content || head) {
814 output_stream.Clear ();
815 if (WorkerRequest != null)
816 output_stream.Flush (WorkerRequest, true); // ignore final_flush here.
821 WriteHeaders (final_flush);
823 if (context != null) {
824 HttpApplication app_instance = context.ApplicationInstance;
825 if (app_instance != null)
826 app_instance.TriggerPreSendRequestContent ();
830 cached_response.SetData (output_stream.GetData ());
832 if (WorkerRequest != null)
833 output_stream.Flush (WorkerRequest, final_flush);
841 public void Pics (string value)
843 AppendHeader ("PICS-Label", value);
846 void Redirect (string url, bool endResponse, int code)
849 throw new ArgumentNullException ("url");
852 throw new HttpException ("Headers have already been sent");
854 if (url.IndexOf ('\n') != -1)
855 throw new ArgumentException ("Redirect URI cannot contain newline characters.", "url");
857 is_request_being_redirected = true;
862 url = ApplyAppPathModifier (url);
864 bool isFullyQualified;
865 if (StrUtils.StartsWith (url, "http:", true) ||
866 StrUtils.StartsWith (url, "https:", true) ||
867 StrUtils.StartsWith (url, "file:", true) ||
868 StrUtils.StartsWith (url, "ftp:", true))
869 isFullyQualified = true;
871 isFullyQualified = false;
873 if (!isFullyQualified) {
874 HttpRuntimeSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime") as HttpRuntimeSection;
875 if (config != null && config.UseFullyQualifiedRedirectUrl) {
876 var ub = new UriBuilder (context.Request.Url);
882 url = ub.Uri.ToString ();
886 redirect_location = url;
888 // Text for browsers that can't handle location header
889 Write ("<html><head><title>Object moved</title></head><body>\r\n");
890 Write ("<h2>Object moved to <a href=\"" + url + "\">here</a></h2>\r\n");
891 Write ("</body><html>\r\n");
895 is_request_being_redirected = false;
898 public void Redirect (string url)
900 Redirect (url, true);
903 public void Redirect (string url, bool endResponse)
905 Redirect (url, endResponse, 302);
908 public void RedirectPermanent (string url)
910 RedirectPermanent (url, true);
913 public void RedirectPermanent (string url, bool endResponse)
915 Redirect (url, endResponse, 301);
918 public void RedirectToRoute (object routeValues)
920 RedirectToRoute ("RedirectToRoute", null, new RouteValueDictionary (routeValues), 302, true);
923 public void RedirectToRoute (RouteValueDictionary routeValues)
925 RedirectToRoute ("RedirectToRoute", null, routeValues, 302, true);
928 public void RedirectToRoute (string routeName)
930 RedirectToRoute ("RedirectToRoute", routeName, null, 302, true);
933 public void RedirectToRoute (string routeName, object routeValues)
935 RedirectToRoute ("RedirectToRoute", routeName, new RouteValueDictionary (routeValues), 302, true);
938 public void RedirectToRoute (string routeName, RouteValueDictionary routeValues)
940 RedirectToRoute ("RedirectToRoute", routeName, routeValues, 302, true);
943 public void RedirectToRoutePermanent (object routeValues)
945 RedirectToRoute ("RedirectToRoutePermanent", null, new RouteValueDictionary (routeValues), 301, false);
948 public void RedirectToRoutePermanent (RouteValueDictionary routeValues)
950 RedirectToRoute ("RedirectToRoutePermanent", null, routeValues, 301, false);
953 public void RedirectToRoutePermanent (string routeName)
955 RedirectToRoute ("RedirectToRoutePermanent", routeName, null, 301, false);
958 public void RedirectToRoutePermanent (string routeName, object routeValues)
960 RedirectToRoute ("RedirectToRoutePermanent", routeName, new RouteValueDictionary (routeValues), 301, false);
963 public void RedirectToRoutePermanent (string routeName, RouteValueDictionary routeValues)
965 RedirectToRoute ("RedirectToRoutePermanent", routeName, routeValues, 301, false);
968 void RedirectToRoute (string callerName, string routeName, RouteValueDictionary routeValues, int redirectCode, bool endResponse)
970 HttpContext ctx = context ?? HttpContext.Current;
971 HttpRequest req = ctx != null ? ctx.Request : null;
974 // Let's emulate .NET
975 throw new NullReferenceException ();
977 VirtualPathData vpd = RouteTable.Routes.GetVirtualPath (req.RequestContext, routeName, routeValues);
978 string redirectUrl = vpd != null ? vpd.VirtualPath : null;
979 if (String.IsNullOrEmpty (redirectUrl))
980 throw new InvalidOperationException ("No matching route found for RedirectToRoute");
982 Redirect (redirectUrl, true, redirectCode);
985 public static void RemoveOutputCacheItem (string path, string providerName)
988 throw new ArgumentNullException ("path");
990 if (path.Length > 0 && path [0] != '/')
991 throw new ArgumentException ("Invalid path for HttpResponse.RemoveOutputCacheItem: '" + path + "'. An absolute virtual path is expected");
993 OutputCache.RemoveFromProvider (path, providerName);
996 public static void RemoveOutputCacheItem (string path)
999 throw new ArgumentNullException ("path");
1001 if (path.Length == 0)
1004 if (path [0] != '/')
1005 throw new ArgumentException ("'" + path + "' is not an absolute virtual path.");
1008 RemoveOutputCacheItem (path, OutputCache.DefaultProviderName);
1010 HttpContext context = HttpContext.Current;
1011 HttpApplication app_instance = context != null ? context.ApplicationInstance : null;
1012 HttpModuleCollection modules = app_instance != null ? app_instance.Modules : null;
1013 OutputCacheModule ocm = modules != null ? modules.Get ("OutputCache") as OutputCacheModule : null;
1014 OutputCacheProvider internalProvider = ocm != null ? ocm.InternalProvider : null;
1015 if (internalProvider == null)
1018 internalProvider.Remove (path);
1022 public void SetCookie (HttpCookie cookie)
1024 AppendCookie (cookie);
1027 public void Write (char ch)
1029 TextWriter writer = Output;
1033 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1038 public void Write (object obj)
1040 TextWriter writer = Output;
1044 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1049 writer.Write (obj.ToString ());
1052 public void Write (string s)
1054 TextWriter writer = Output;
1058 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1063 public void Write (char [] buffer, int index, int count)
1065 TextWriter writer = Output;
1069 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1071 writer.Write (buffer, index, count);
1074 internal void WriteFile (FileStream fs, long offset, long size)
1076 byte [] buffer = new byte [32*1024];
1079 fs.Position = offset;
1083 while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
1085 output_stream.Write (buffer, 0, n);
1089 public void WriteFile (string filename)
1091 WriteFile (filename, false);
1094 public void WriteFile (string filename, bool readIntoMemory)
1096 if (filename == null)
1097 throw new ArgumentNullException ("filename");
1099 if (readIntoMemory){
1100 using (FileStream fs = File.OpenRead (filename))
1101 WriteFile (fs, 0, fs.Length);
1103 FileInfo fi = new FileInfo (filename);
1104 output_stream.WriteFile (filename, 0, fi.Length);
1109 output_stream.ApplyFilter (false);
1114 public void WriteFile (IntPtr fileHandle, long offset, long size) {
1115 throw new PlatformNotSupportedException("IntPtr not supported");
1118 public void WriteFile (IntPtr fileHandle, long offset, long size)
1121 throw new ArgumentNullException ("offset can not be negative");
1123 throw new ArgumentNullException ("size can not be negative");
1128 // Note: this .ctor will throw a SecurityException if the caller
1129 // doesn't have the UnmanagedCode permission
1130 using (FileStream fs = new FileStream (fileHandle, FileAccess.Read))
1131 WriteFile (fs, offset, size);
1135 output_stream.ApplyFilter (false);
1140 public void WriteFile (string filename, long offset, long size)
1142 if (filename == null)
1143 throw new ArgumentNullException ("filename");
1145 throw new ArgumentNullException ("offset can not be negative");
1147 throw new ArgumentNullException ("size can not be negative");
1152 FileStream fs = File.OpenRead (filename);
1153 WriteFile (fs, offset, size);
1158 output_stream.ApplyFilter (false);
1162 public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
1164 // Emulation of .NET behavior
1165 if (callback == null)
1166 throw new NullReferenceException ();
1168 object target = callback.Target;
1169 if (target != null && target.GetType () == typeof (Control))
1170 throw new ArgumentException ("callback");
1172 string s = callback (context);
1178 Cache.Cacheability = HttpCacheability.Server;
1180 if (WorkerRequest == null)
1181 Write (s); // better this than nothing
1183 byte[] bytes = WebEncoding.ResponseEncoding.GetBytes (s);
1184 WorkerRequest.SendResponseFromMemory (bytes, bytes.Length);
1187 cached_response.SetData (callback);
1191 // Like WriteFile, but never buffers, so we manually Flush here
1193 public void TransmitFile (string filename)
1195 if (filename == null)
1196 throw new ArgumentNullException ("filename");
1198 TransmitFile (filename, false);
1201 internal void TransmitFile (string filename, bool final_flush)
1203 FileInfo fi = new FileInfo (filename);
1204 using (Stream s = fi.OpenRead ()); // Just check if we can read.
1205 output_stream.WriteFile (filename, 0, fi.Length);
1206 output_stream.ApplyFilter (final_flush);
1207 Flush (final_flush);
1210 public void TransmitFile (string filename, long offset, long length)
1212 output_stream.WriteFile (filename, offset, length);
1213 output_stream.ApplyFilter (false);
1217 internal void TransmitFile (VirtualFile vf)
1219 TransmitFile (vf, false);
1222 const int bufLen = 65535;
1223 internal void TransmitFile (VirtualFile vf, bool final_flush)
1226 throw new ArgumentNullException ("vf");
1228 if (vf is DefaultVirtualFile) {
1229 TransmitFile (HostingEnvironment.MapPath (vf.VirtualPath), final_flush);
1233 byte[] buf = new byte [bufLen];
1234 using (Stream s = vf.Open ()) {
1236 while ((readBytes = s.Read (buf, 0, bufLen)) > 0) {
1237 output_stream.Write (buf, 0, readBytes);
1238 output_stream.ApplyFilter (final_flush);
1246 #region Session state support
1247 internal void SetAppPathModifier (string app_modifier)
1249 app_path_mod = app_modifier;
1253 #region Cache Support
1254 internal void SetCachedHeaders (NameValueCollection headers)
1256 cached_headers = headers;
1260 internal bool IsCached {
1261 get { return cached_response != null; }
1264 cached_response = new CachedRawResponse (cache_policy);
1266 cached_response = null;
1270 public HttpCachePolicy Cache {
1272 if (cache_policy == null)
1273 cache_policy = new HttpCachePolicy ();
1275 return cache_policy;
1279 internal CachedRawResponse GetCachedResponse ()
1281 if (cached_response != null) {
1282 cached_response.StatusCode = StatusCode;
1283 cached_response.StatusDescription = StatusDescription;
1286 return cached_response;
1290 // This is one of the old ASP compatibility methods, the real cache
1291 // control is in the Cache property, and this is a second class citizen
1293 public string CacheControl {
1295 if (value == null || value == "") {
1296 Cache.SetCacheability (HttpCacheability.NoCache);
1297 user_cache_control = null;
1298 } else if (String.Compare (value, "public", true, Helpers.InvariantCulture) == 0) {
1299 Cache.SetCacheability (HttpCacheability.Public);
1300 user_cache_control = "public";
1301 } else if (String.Compare (value, "private", true, Helpers.InvariantCulture) == 0) {
1302 Cache.SetCacheability (HttpCacheability.Private);
1303 user_cache_control = "private";
1304 } else if (String.Compare (value, "no-cache", true, Helpers.InvariantCulture) == 0) {
1305 Cache.SetCacheability (HttpCacheability.NoCache);
1306 user_cache_control = "no-cache";
1308 throw new ArgumentException ("CacheControl property only allows `public', " +
1309 "`private' or no-cache, for different uses, use " +
1310 "Response.AppendHeader");
1313 get { return (user_cache_control != null) ? user_cache_control : "private"; }
1317 internal int GetOutputByteCount ()
1319 return output_stream.GetTotalLength ();
1322 internal void ReleaseResources ()
1324 output_stream.ReleaseResources (true);
1325 output_stream = null;
1332 static class FlagEnd
1334 public static readonly object Value = new object ();