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;
103 internal bool suppress_content;
109 bool is_request_being_redirected;
110 Encoding headerEncoding;
112 internal HttpResponse ()
114 output_stream = new HttpResponseStream (this);
115 writer = new HttpWriter (this);
118 public HttpResponse (TextWriter writer) : this ()
121 this.writer = writer;
124 internal HttpResponse (HttpWorkerRequest worker_request, HttpContext context) : this ()
126 WorkerRequest = worker_request;
127 this.context = context;
130 if (worker_request != null && worker_request.GetHttpVersion () == "HTTP/1.1") {
131 string gi = worker_request.GetServerVariable ("GATEWAY_INTERFACE");
132 use_chunked = (String.IsNullOrEmpty (gi) ||
133 !gi.StartsWith ("cgi", StringComparison.OrdinalIgnoreCase));
138 writer = new HttpWriter (this);
141 internal TextWriter SetTextWriter (TextWriter writer)
143 TextWriter prev = this.writer;
144 this.writer = writer;
148 internal string VersionHeader {
150 if (!version_header_checked && version_header == null) {
151 version_header_checked = true;
152 HttpRuntimeSection config = HttpRuntime.Section;
153 if (config != null && config.EnableVersionHeader)
154 version_header = Environment.Version.ToString (3);
157 return version_header;
161 internal HttpContext Context {
162 get { return context; }
163 set { context = value; }
166 internal string[] FileDependencies {
168 if (fileDependencies == null || fileDependencies.Count == 0)
169 return new string[0] {};
170 return (string[]) fileDependencies.ToArray (typeof (string));
174 ArrayList FileDependenciesArray {
176 if (fileDependencies == null)
177 fileDependencies = new ArrayList ();
178 return fileDependencies;
192 public bool BufferOutput {
203 // Use the default from <globalization> section if the client has not set the encoding
205 public Encoding ContentEncoding {
207 if (encoding == null) {
208 if (context != null) {
209 string client_content_type = context.Request.ContentType;
210 string parameter = HttpRequest.GetParameter (client_content_type, "; charset=");
211 if (parameter != null) {
213 // Do what the #1 web server does
214 encoding = Encoding.GetEncoding (parameter);
219 if (encoding == null)
220 encoding = WebEncoding.ResponseEncoding;
227 throw new ArgumentException ("ContentEncoding can not be null");
230 HttpWriter http_writer = writer as HttpWriter;
231 if (http_writer != null)
232 http_writer.SetEncoding (encoding);
236 public string ContentType {
242 content_type = value;
246 public string Charset {
249 charset = ContentEncoding.WebName;
260 public HttpCookieCollection Cookies {
263 cookies = new HttpCookieCollection (true, false);
270 if (cache_policy == null)
273 return cache_policy.ExpireMinutes ();
277 Cache.SetExpires (DateTime.Now + new TimeSpan (0, value, 0));
281 public DateTime ExpiresAbsolute {
283 return Cache.Expires;
287 Cache.SetExpires (value);
291 public Stream Filter {
293 if (WorkerRequest == null)
296 return output_stream.Filter;
300 output_stream.Filter = value;
304 public Encoding HeaderEncoding {
306 if (headerEncoding == null) {
307 GlobalizationSection gs = WebConfigurationManager.SafeGetSection ("system.web/globalization", typeof (GlobalizationSection)) as GlobalizationSection;
310 headerEncoding = Encoding.UTF8;
312 headerEncoding = gs.ResponseHeaderEncoding;
313 if (headerEncoding == Encoding.Unicode)
314 throw new HttpException ("HeaderEncoding must not be Unicode");
317 return headerEncoding;
321 throw new HttpException ("headers have already been sent");
323 throw new ArgumentNullException ("HeaderEncoding");
324 if (value == Encoding.Unicode)
325 throw new HttpException ("HeaderEncoding must not be Unicode");
326 headerEncoding = value;
330 public NameValueCollection Headers {
333 headers = new HttpHeaderCollection ();
340 public bool IsClientConnected {
342 if (WorkerRequest == null)
343 return true; // yep that's true
345 return WorkerRequest.IsClientConnected ();
349 public bool IsRequestBeingRedirected {
350 get { return is_request_being_redirected; }
353 public TextWriter Output {
358 set { writer = value; }
362 public Stream OutputStream {
364 return output_stream;
368 public string RedirectLocation {
370 return redirect_location;
374 redirect_location = value;
378 public string Status {
379 get { return String.Concat (status_code.ToString (), " ", StatusDescription); }
382 int p = value.IndexOf (' ');
384 throw new HttpException ("Invalid format for the Status property");
386 string s = value.Substring (0, p);
388 if (!Int32.TryParse (s, out status_code))
389 throw new HttpException ("Invalid format for the Status property");
391 status_description = value.Substring (p+1);
395 // We ignore the two properties on Mono as they are for use with IIS7, but there is
396 // no point in throwing PlatformNotSupportedException. We might find a use for them
398 public int SubStatusCode {
403 public bool TrySkipIisCustomErrors {
408 public int StatusCode {
415 throw new HttpException ("headers have already been sent");
418 status_description = null;
422 public string StatusDescription {
424 if (status_description == null)
425 status_description = HttpWorkerRequest.GetStatusDescription (status_code);
427 return status_description;
432 throw new HttpException ("headers have already been sent");
434 status_description = value;
438 public bool SuppressContent {
440 return suppress_content;
444 suppress_content = value;
448 [MonoTODO ("Not implemented")]
449 public void AddCacheDependency (CacheDependency[] dependencies)
451 throw new NotImplementedException ();
454 [MonoTODO ("Not implemented")]
455 public void AddCacheItemDependencies (string[] cacheKeys)
457 throw new NotImplementedException ();
460 [MonoTODO("Currently does nothing")]
461 public void AddCacheItemDependencies (ArrayList cacheKeys)
463 // TODO: talk to jackson about the cache
466 [MonoTODO("Currently does nothing")]
467 public void AddCacheItemDependency (string cacheKey)
469 // TODO: talk to jackson about the cache
472 public void AddFileDependencies (ArrayList filenames)
474 if (filenames == null || filenames.Count == 0)
476 FileDependenciesArray.AddRange (filenames);
479 public void AddFileDependencies (string[] filenames)
481 if (filenames == null || filenames.Length == 0)
483 FileDependenciesArray.AddRange (filenames);
486 public void AddFileDependency (string filename)
488 if (filename == null || filename == String.Empty)
490 FileDependenciesArray.Add (filename);
493 public void AddHeader (string name, string value)
495 AppendHeader (name, value);
498 public void AppendCookie (HttpCookie cookie)
500 Cookies.Add (cookie);
505 // Special case for Content-Length, Content-Type, Transfer-Encoding and Cache-Control
508 public void AppendHeader (string name, string value)
511 throw new HttpException ("Headers have been already sent");
513 if (String.Compare (name, "content-length", StringComparison.OrdinalIgnoreCase) == 0){
514 content_length = (long) UInt64.Parse (value);
520 if (String.Compare (name, "content-type", StringComparison.OrdinalIgnoreCase) == 0){
526 if (String.Compare (name, "transfer-encoding", StringComparison.OrdinalIgnoreCase) == 0){
527 transfer_encoding = value;
533 if (String.Compare (name, "cache-control", StringComparison.OrdinalIgnoreCase) == 0){
534 user_cache_control = value;
538 Headers.Add (name, value);
541 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
542 public void AppendToLog (string param)
544 Console.Write ("System.Web: ");
545 Console.WriteLine (param);
548 public string ApplyAppPathModifier (string virtualPath)
550 if (virtualPath == null || context == null)
553 if (virtualPath.Length == 0)
554 return context.Request.RootVirtualDir;
556 if (UrlUtils.IsRelativeUrl (virtualPath)) {
557 virtualPath = UrlUtils.Combine (context.Request.RootVirtualDir, virtualPath);
558 } else if (UrlUtils.IsRooted (virtualPath)) {
559 virtualPath = UrlUtils.Canonic (virtualPath);
562 bool cookieless = false;
563 SessionStateSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/sessionState") as SessionStateSection;
564 cookieless = SessionStateModule.IsCookieLess (context, config);
569 if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) < 0) {
570 if (UrlUtils.HasSessionId (virtualPath))
571 virtualPath = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (virtualPath), virtualPath);
572 return UrlUtils.InsertSessionId (app_path_mod, virtualPath);
578 public void BinaryWrite (byte [] buffer)
580 output_stream.Write (buffer, 0, buffer.Length);
583 internal void BinaryWrite (byte [] buffer, int start, int len)
585 output_stream.Write (buffer, start, len);
593 public void ClearContent ()
595 output_stream.Clear ();
599 public void ClearHeaders ()
602 throw new HttpException ("headers have been already sent");
604 // Reset the special case headers.
606 content_type = "text/html";
607 transfer_encoding = null;
608 user_cache_control = "private";
609 if (cache_policy != null)
610 cache_policy.Cacheability = HttpCacheability.Private;
616 internal bool HeadersSent {
626 if (WorkerRequest != null)
627 WorkerRequest.CloseConnection ();
631 public void DisableKernelCache ()
633 // does nothing in Mono
641 if (context.TimeoutPossible) {
642 Thread.CurrentThread.Abort (FlagEnd.Value);
644 // If this is called from an async event, signal the completion
646 HttpApplication app_instance = context.ApplicationInstance;
647 if (app_instance != null)
648 app_instance.CompleteRequest ();
655 // Transfer-Encoding (chunked)
658 void AddHeadersNoCache (NameValueCollection write_headers, bool final_flush)
665 write_headers.Add ("Transfer-Encoding", "chunked");
666 else if (transfer_encoding != null)
667 write_headers.Add ("Transfer-Encoding", transfer_encoding);
669 if (redirect_location != null)
670 write_headers.Add ("Location", redirect_location);
673 string vh = VersionHeader;
675 write_headers.Add ("X-AspNet-Version", vh);
678 // If Content-Length is set.
680 if (content_length >= 0) {
681 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
682 content_length.ToString (Helpers.InvariantCulture));
683 } else if (BufferOutput) {
686 // If we are buffering and this is the last flush, not a middle-flush,
687 // we know the content-length.
689 content_length = output_stream.total;
690 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
691 content_length.ToString (Helpers.InvariantCulture));
694 // We are buffering, and this is a flush in the middle.
695 // If we are not chunked, we need to set "Connection: close".
698 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
703 // If the content-length is not set, and we are not buffering, we must
707 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
713 // Cache Control, the cache policy takes precedence over the cache_control property.
715 if (cache_policy != null)
716 cache_policy.SetHeaders (this, headers);
718 write_headers.Add ("Cache-Control", CacheControl);
723 if (content_type != null){
724 string header = content_type;
726 if (charset_set || header == "text/plain" || header == "text/html") {
727 if (header.IndexOf ("charset=") == -1) {
728 if (charset == null || charset == "")
729 charset = ContentEncoding.HeaderName;
730 header += "; charset=" + charset;
734 write_headers.Add ("Content-Type", header);
737 if (cookies != null && cookies.Count != 0){
738 int n = cookies.Count;
739 for (int i = 0; i < n; i++)
740 write_headers.Add ("Set-Cookie", cookies.Get (i).GetCookieHeaderValue ());
742 // For J2EE Portal support emulate cookies by storing them in the session.
743 context.Request.SetSessionCookiesForPortal (cookies);
748 internal void WriteHeaders (bool final_flush)
756 if (context != null) {
757 HttpApplication app_instance = context.ApplicationInstance;
758 if (app_instance != null)
759 app_instance.TriggerPreSendRequestHeaders ();
764 if (cached_response != null)
765 cached_response.SetHeaders (headers);
767 // If this page is cached use the cached headers
768 // instead of the standard headers
769 NameValueCollection write_headers;
770 if (cached_headers != null)
771 write_headers = cached_headers;
773 write_headers = Headers;
774 AddHeadersNoCache (write_headers, final_flush);
777 if (WorkerRequest != null)
778 WorkerRequest.SendStatus (status_code, StatusDescription);
780 if (WorkerRequest != null) {
785 for (int i = 0; i < write_headers.Count; i++) {
786 header_name = write_headers.GetKey (i);
787 header_index = HttpWorkerRequest.GetKnownResponseHeaderIndex (header_name);
788 values = write_headers.GetValues (i);
792 foreach (string val in values) {
793 if (header_index > -1)
794 WorkerRequest.SendKnownResponseHeader (header_index, val);
796 WorkerRequest.SendUnknownResponseHeader (header_name, val);
802 internal void DoFilter (bool close)
804 if (output_stream.HaveFilter && context != null && context.Error == null)
805 output_stream.ApplyFilter (close);
808 internal void Flush (bool final_flush)
811 throw new HttpException ("Server cannot flush a completed response");
813 DoFilter (final_flush);
815 if (final_flush || status_code != 200)
819 bool head = ((context != null) && (context.Request.HttpMethod == "HEAD"));
820 if (suppress_content || head) {
823 output_stream.Clear ();
824 if (WorkerRequest != null)
825 output_stream.Flush (WorkerRequest, true); // ignore final_flush here.
829 completed = final_flush;
832 WriteHeaders (final_flush);
834 if (context != null) {
835 HttpApplication app_instance = context.ApplicationInstance;
836 if (app_instance != null)
837 app_instance.TriggerPreSendRequestContent ();
841 cached_response.SetData (output_stream.GetData ());
843 if (WorkerRequest != null)
844 output_stream.Flush (WorkerRequest, final_flush);
852 public void Pics (string value)
854 AppendHeader ("PICS-Label", value);
857 void Redirect (string url, bool endResponse, int code)
860 throw new ArgumentNullException ("url");
863 throw new HttpException ("Headers have already been sent");
865 if (url.IndexOf ('\n') != -1)
866 throw new ArgumentException ("Redirect URI cannot contain newline characters.", "url");
868 is_request_being_redirected = true;
873 url = ApplyAppPathModifier (url);
875 bool isFullyQualified;
876 if (StrUtils.StartsWith (url, "http:", true) ||
877 StrUtils.StartsWith (url, "https:", true) ||
878 StrUtils.StartsWith (url, "file:", true) ||
879 StrUtils.StartsWith (url, "ftp:", true))
880 isFullyQualified = true;
882 isFullyQualified = false;
884 if (!isFullyQualified) {
885 HttpRuntimeSection config = HttpRuntime.Section;
886 if (config != null && config.UseFullyQualifiedRedirectUrl) {
887 var ub = new UriBuilder (context.Request.Url);
888 int qpos = url.IndexOf ('?');
893 ub.Path = url.Substring (0, qpos);
894 ub.Query = url.Substring (qpos + 1);
899 url = ub.Uri.ToString ();
903 redirect_location = url;
905 // Text for browsers that can't handle location header
906 Write ("<html><head><title>Object moved</title></head><body>\r\n");
907 Write ("<h2>Object moved to <a href=\"" + url + "\">here</a></h2>\r\n");
908 Write ("</body><html>\r\n");
912 is_request_being_redirected = false;
915 public void Redirect (string url)
917 Redirect (url, true);
920 public void Redirect (string url, bool endResponse)
922 Redirect (url, endResponse, 302);
925 public void RedirectPermanent (string url)
927 RedirectPermanent (url, true);
930 public void RedirectPermanent (string url, bool endResponse)
932 Redirect (url, endResponse, 301);
935 public void RedirectToRoute (object routeValues)
937 RedirectToRoute ("RedirectToRoute", null, new RouteValueDictionary (routeValues), 302, true);
940 public void RedirectToRoute (RouteValueDictionary routeValues)
942 RedirectToRoute ("RedirectToRoute", null, routeValues, 302, true);
945 public void RedirectToRoute (string routeName)
947 RedirectToRoute ("RedirectToRoute", routeName, null, 302, true);
950 public void RedirectToRoute (string routeName, object routeValues)
952 RedirectToRoute ("RedirectToRoute", routeName, new RouteValueDictionary (routeValues), 302, true);
955 public void RedirectToRoute (string routeName, RouteValueDictionary routeValues)
957 RedirectToRoute ("RedirectToRoute", routeName, routeValues, 302, true);
960 public void RedirectToRoutePermanent (object routeValues)
962 RedirectToRoute ("RedirectToRoutePermanent", null, new RouteValueDictionary (routeValues), 301, false);
965 public void RedirectToRoutePermanent (RouteValueDictionary routeValues)
967 RedirectToRoute ("RedirectToRoutePermanent", null, routeValues, 301, false);
970 public void RedirectToRoutePermanent (string routeName)
972 RedirectToRoute ("RedirectToRoutePermanent", routeName, null, 301, false);
975 public void RedirectToRoutePermanent (string routeName, object routeValues)
977 RedirectToRoute ("RedirectToRoutePermanent", routeName, new RouteValueDictionary (routeValues), 301, false);
980 public void RedirectToRoutePermanent (string routeName, RouteValueDictionary routeValues)
982 RedirectToRoute ("RedirectToRoutePermanent", routeName, routeValues, 301, false);
985 void RedirectToRoute (string callerName, string routeName, RouteValueDictionary routeValues, int redirectCode, bool endResponse)
987 HttpContext ctx = context ?? HttpContext.Current;
988 HttpRequest req = ctx != null ? ctx.Request : null;
991 // Let's emulate .NET
992 throw new NullReferenceException ();
994 VirtualPathData vpd = RouteTable.Routes.GetVirtualPath (req.RequestContext, routeName, routeValues);
995 string redirectUrl = vpd != null ? vpd.VirtualPath : null;
996 if (String.IsNullOrEmpty (redirectUrl))
997 throw new InvalidOperationException ("No matching route found for RedirectToRoute");
999 Redirect (redirectUrl, true, redirectCode);
1002 public static void RemoveOutputCacheItem (string path, string providerName)
1005 throw new ArgumentNullException ("path");
1007 if (path.Length > 0 && path [0] != '/')
1008 throw new ArgumentException ("Invalid path for HttpResponse.RemoveOutputCacheItem: '" + path + "'. An absolute virtual path is expected");
1010 OutputCache.RemoveFromProvider (path, providerName);
1013 public static void RemoveOutputCacheItem (string path)
1016 throw new ArgumentNullException ("path");
1018 if (path.Length == 0)
1021 if (path [0] != '/')
1022 throw new ArgumentException ("'" + path + "' is not an absolute virtual path.");
1025 RemoveOutputCacheItem (path, OutputCache.DefaultProviderName);
1027 HttpContext context = HttpContext.Current;
1028 HttpApplication app_instance = context != null ? context.ApplicationInstance : null;
1029 HttpModuleCollection modules = app_instance != null ? app_instance.Modules : null;
1030 OutputCacheModule ocm = modules != null ? modules.Get ("OutputCache") as OutputCacheModule : null;
1031 OutputCacheProvider internalProvider = ocm != null ? ocm.InternalProvider : null;
1032 if (internalProvider == null)
1035 internalProvider.Remove (path);
1039 public void SetCookie (HttpCookie cookie)
1041 AppendCookie (cookie);
1044 public void Write (char ch)
1046 TextWriter writer = Output;
1050 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1055 public void Write (object obj)
1057 TextWriter writer = Output;
1061 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1066 writer.Write (obj.ToString ());
1069 public void Write (string s)
1071 TextWriter writer = Output;
1075 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1080 public void Write (char [] buffer, int index, int count)
1082 TextWriter writer = Output;
1086 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1088 writer.Write (buffer, index, count);
1091 bool IsFileSystemDirSeparator (char ch)
1093 return ch == '\\' || ch == '/';
1096 string GetNormalizedFileName (string fn)
1098 if (String.IsNullOrEmpty (fn))
1101 // On Linux we don't change \ to / since filenames with \ are valid. We also
1102 // don't remove drive: designator for the same reason.
1103 int len = fn.Length;
1104 if (len >= 3 && fn [1] == ':' && IsFileSystemDirSeparator (fn [2]))
1105 return Path.GetFullPath (fn); // drive-qualified absolute file path
1107 bool startsWithDirSeparator = IsFileSystemDirSeparator (fn [0]);
1108 if (len >= 2 && startsWithDirSeparator && IsFileSystemDirSeparator (fn [1]))
1109 return Path.GetFullPath (fn); // UNC path
1111 if (!startsWithDirSeparator) {
1112 HttpContext ctx = context ?? HttpContext.Current;
1113 HttpRequest req = ctx != null ? ctx.Request : null;
1116 return req.MapPath (fn);
1119 return fn; // Or should we rather throw?
1122 internal void WriteFile (FileStream fs, long offset, long size)
1124 byte [] buffer = new byte [32*1024];
1127 fs.Position = offset;
1131 while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
1133 output_stream.Write (buffer, 0, n);
1137 public void WriteFile (string filename)
1139 WriteFile (filename, false);
1142 public void WriteFile (string filename, bool readIntoMemory)
1144 if (filename == null)
1145 throw new ArgumentNullException ("filename");
1147 string fn = GetNormalizedFileName (filename);
1148 if (readIntoMemory){
1149 using (FileStream fs = File.OpenRead (fn))
1150 WriteFile (fs, 0, fs.Length);
1152 FileInfo fi = new FileInfo (fn);
1153 output_stream.WriteFile (fn, 0, fi.Length);
1158 output_stream.ApplyFilter (false);
1163 public void WriteFile (IntPtr fileHandle, long offset, long size) {
1164 throw new PlatformNotSupportedException("IntPtr not supported");
1167 public void WriteFile (IntPtr fileHandle, long offset, long size)
1170 throw new ArgumentNullException ("offset can not be negative");
1172 throw new ArgumentNullException ("size can not be negative");
1177 // Note: this .ctor will throw a SecurityException if the caller
1178 // doesn't have the UnmanagedCode permission
1179 using (FileStream fs = new FileStream (fileHandle, FileAccess.Read))
1180 WriteFile (fs, offset, size);
1184 output_stream.ApplyFilter (false);
1189 public void WriteFile (string filename, long offset, long size)
1191 if (filename == null)
1192 throw new ArgumentNullException ("filename");
1194 throw new ArgumentNullException ("offset can not be negative");
1196 throw new ArgumentNullException ("size can not be negative");
1201 FileStream fs = File.OpenRead (filename);
1202 WriteFile (fs, offset, size);
1207 output_stream.ApplyFilter (false);
1211 public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
1213 // Emulation of .NET behavior
1214 if (callback == null)
1215 throw new NullReferenceException ();
1217 object target = callback.Target;
1218 if (target != null && target.GetType () == typeof (Control))
1219 throw new ArgumentException ("callback");
1221 string s = callback (context);
1227 Cache.Cacheability = HttpCacheability.Server;
1229 if (WorkerRequest == null)
1230 Write (s); // better this than nothing
1232 byte[] bytes = WebEncoding.ResponseEncoding.GetBytes (s);
1233 WorkerRequest.SendResponseFromMemory (bytes, bytes.Length);
1236 cached_response.SetData (callback);
1240 // Like WriteFile, but never buffers, so we manually Flush here
1242 public void TransmitFile (string filename)
1244 if (filename == null)
1245 throw new ArgumentNullException ("filename");
1247 TransmitFile (filename, false);
1250 internal void TransmitFile (string filename, bool final_flush)
1252 FileInfo fi = new FileInfo (filename);
1253 using (Stream s = fi.OpenRead ()); // Just check if we can read.
1254 output_stream.WriteFile (filename, 0, fi.Length);
1255 output_stream.ApplyFilter (final_flush);
1256 Flush (final_flush);
1259 public void TransmitFile (string filename, long offset, long length)
1261 output_stream.WriteFile (filename, offset, length);
1262 output_stream.ApplyFilter (false);
1266 internal void TransmitFile (VirtualFile vf)
1268 TransmitFile (vf, false);
1271 const int bufLen = 65535;
1272 internal void TransmitFile (VirtualFile vf, bool final_flush)
1275 throw new ArgumentNullException ("vf");
1277 if (vf is DefaultVirtualFile) {
1278 TransmitFile (HostingEnvironment.MapPath (vf.VirtualPath), final_flush);
1282 byte[] buf = new byte [bufLen];
1283 using (Stream s = vf.Open ()) {
1285 while ((readBytes = s.Read (buf, 0, bufLen)) > 0) {
1286 output_stream.Write (buf, 0, readBytes);
1287 output_stream.ApplyFilter (final_flush);
1295 #region Session state support
1296 internal void SetAppPathModifier (string app_modifier)
1298 app_path_mod = app_modifier;
1302 #region Cache Support
1303 internal void SetCachedHeaders (NameValueCollection headers)
1305 cached_headers = headers;
1309 internal bool IsCached {
1310 get { return cached_response != null; }
1313 cached_response = new CachedRawResponse (cache_policy);
1315 cached_response = null;
1319 public HttpCachePolicy Cache {
1321 if (cache_policy == null)
1322 cache_policy = new HttpCachePolicy ();
1324 return cache_policy;
1328 internal CachedRawResponse GetCachedResponse ()
1330 if (cached_response != null) {
1331 cached_response.StatusCode = StatusCode;
1332 cached_response.StatusDescription = StatusDescription;
1335 return cached_response;
1339 // This is one of the old ASP compatibility methods, the real cache
1340 // control is in the Cache property, and this is a second class citizen
1342 public string CacheControl {
1344 if (value == null || value == "") {
1345 Cache.SetCacheability (HttpCacheability.NoCache);
1346 user_cache_control = null;
1347 } else if (String.Compare (value, "public", true, Helpers.InvariantCulture) == 0) {
1348 Cache.SetCacheability (HttpCacheability.Public);
1349 user_cache_control = "public";
1350 } else if (String.Compare (value, "private", true, Helpers.InvariantCulture) == 0) {
1351 Cache.SetCacheability (HttpCacheability.Private);
1352 user_cache_control = "private";
1353 } else if (String.Compare (value, "no-cache", true, Helpers.InvariantCulture) == 0) {
1354 Cache.SetCacheability (HttpCacheability.NoCache);
1355 user_cache_control = "no-cache";
1357 throw new ArgumentException ("CacheControl property only allows `public', " +
1358 "`private' or no-cache, for different uses, use " +
1359 "Response.AppendHeader");
1362 get { return (user_cache_control != null) ? user_cache_control : "private"; }
1366 internal int GetOutputByteCount ()
1368 return output_stream.GetTotalLength ();
1371 internal void ReleaseResources ()
1373 if (output_stream != null)
1374 output_stream.ReleaseResources (true);
1386 static class FlagEnd
1388 public static readonly object Value = new object ();