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;
46 using System.Web.Routing;
50 // CAS - no InheritanceDemand here as the class is sealed
51 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
52 public sealed partial class HttpResponse
54 internal HttpWorkerRequest WorkerRequest;
55 internal HttpResponseStream output_stream;
56 internal bool buffer = true;
58 ArrayList fileDependencies;
62 HttpCachePolicy cache_policy;
64 HttpCookieCollection cookies;
66 int status_code = 200;
67 string status_description = "OK";
69 string content_type = "text/html";
72 CachedRawResponse cached_response;
73 string user_cache_control = "private";
74 string redirect_location;
75 string version_header;
76 bool version_header_checked;
79 // Negative Content-Length means we auto-compute the size of content-length
80 // can be overwritten with AppendHeader ("Content-Length", value)
82 long content_length = -1;
85 // The list of the headers that we will send back to the client, except
86 // the headers that we compute here.
89 HttpHeaderCollection headers;
91 NameValueCollection cached_headers;
94 // Transfer encoding state
96 string transfer_encoding;
97 internal bool use_chunked;
101 internal bool suppress_content;
107 bool is_request_being_redirected;
108 Encoding headerEncoding;
110 internal HttpResponse ()
112 output_stream = new HttpResponseStream (this);
113 writer = new HttpWriter (this);
116 public HttpResponse (TextWriter writer) : this ()
119 this.writer = writer;
122 internal HttpResponse (HttpWorkerRequest worker_request, HttpContext context) : this ()
124 WorkerRequest = worker_request;
125 this.context = context;
127 if (worker_request != null && worker_request.GetHttpVersion () == "HTTP/1.1") {
128 string gi = worker_request.GetServerVariable ("GATEWAY_INTERFACE");
129 use_chunked = (String.IsNullOrEmpty (gi) ||
130 !gi.StartsWith ("cgi", StringComparison.OrdinalIgnoreCase));
134 writer = new HttpWriter (this);
137 internal TextWriter SetTextWriter (TextWriter writer)
139 TextWriter prev = this.writer;
140 this.writer = writer;
144 internal string VersionHeader {
146 if (!version_header_checked && version_header == null) {
147 version_header_checked = true;
148 HttpRuntimeSection config = HttpRuntime.Section;
149 if (config != null && config.EnableVersionHeader)
150 version_header = Environment.Version.ToString (3);
153 return version_header;
157 internal HttpContext Context {
158 get { return context; }
159 set { context = value; }
162 internal string[] FileDependencies {
164 if (fileDependencies == null || fileDependencies.Count == 0)
165 return new string[0] {};
166 return (string[]) fileDependencies.ToArray (typeof (string));
170 ArrayList FileDependenciesArray {
172 if (fileDependencies == null)
173 fileDependencies = new ArrayList ();
174 return fileDependencies;
188 public bool BufferOutput {
199 // Use the default from <globalization> section if the client has not set the encoding
201 public Encoding ContentEncoding {
203 if (encoding == null) {
204 if (context != null) {
205 string client_content_type = context.Request.ContentType;
206 string parameter = HttpRequest.GetParameter (client_content_type, "; charset=");
207 if (parameter != null) {
209 // Do what the #1 web server does
210 encoding = Encoding.GetEncoding (parameter);
215 if (encoding == null)
216 encoding = WebEncoding.ResponseEncoding;
223 throw new ArgumentException ("ContentEncoding can not be null");
226 HttpWriter http_writer = writer as HttpWriter;
227 if (http_writer != null)
228 http_writer.SetEncoding (encoding);
232 public string ContentType {
238 content_type = value;
242 public string Charset {
245 charset = ContentEncoding.WebName;
256 public HttpCookieCollection Cookies {
259 cookies = new HttpCookieCollection (true, false);
266 if (cache_policy == null)
269 return cache_policy.ExpireMinutes ();
273 Cache.SetExpires (DateTime.Now + new TimeSpan (0, value, 0));
277 public DateTime ExpiresAbsolute {
279 return Cache.Expires;
283 Cache.SetExpires (value);
287 public Stream Filter {
289 if (WorkerRequest == null)
292 return output_stream.Filter;
296 output_stream.Filter = value;
300 public Encoding HeaderEncoding {
302 if (headerEncoding == null) {
303 GlobalizationSection gs = WebConfigurationManager.SafeGetSection ("system.web/globalization", typeof (GlobalizationSection)) as GlobalizationSection;
306 headerEncoding = Encoding.UTF8;
308 headerEncoding = gs.ResponseHeaderEncoding;
309 if (headerEncoding == Encoding.Unicode)
310 throw new HttpException ("HeaderEncoding must not be Unicode");
313 return headerEncoding;
317 throw new HttpException ("headers have already been sent");
319 throw new ArgumentNullException ("HeaderEncoding");
320 if (value == Encoding.Unicode)
321 throw new HttpException ("HeaderEncoding must not be Unicode");
322 headerEncoding = value;
326 public NameValueCollection Headers {
329 headers = new HttpHeaderCollection ();
336 public bool IsClientConnected {
338 if (WorkerRequest == null)
339 return true; // yep that's true
341 return WorkerRequest.IsClientConnected ();
345 public bool IsRequestBeingRedirected {
346 get { return is_request_being_redirected; }
349 public TextWriter Output {
353 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 SuppressFormsAuthenticationRedirect {
402 public bool TrySkipIisCustomErrors {
407 public int StatusCode {
414 throw new HttpException ("headers have already been sent");
417 status_description = null;
421 public string StatusDescription {
423 if (status_description == null)
424 status_description = HttpWorkerRequest.GetStatusDescription (status_code);
426 return status_description;
431 throw new HttpException ("headers have already been sent");
433 status_description = value;
437 public bool SuppressContent {
439 return suppress_content;
443 suppress_content = value;
447 [MonoTODO ("Not implemented")]
448 public void AddCacheDependency (params CacheDependency[] dependencies)
450 throw new NotImplementedException ();
453 [MonoTODO ("Not implemented")]
454 public void AddCacheItemDependencies (string[] cacheKeys)
456 throw new NotImplementedException ();
459 [MonoTODO("Currently does nothing")]
460 public void AddCacheItemDependencies (ArrayList cacheKeys)
462 // TODO: talk to jackson about the cache
465 [MonoTODO("Currently does nothing")]
466 public void AddCacheItemDependency (string cacheKey)
468 // TODO: talk to jackson about the cache
471 public void AddFileDependencies (ArrayList filenames)
473 if (filenames == null || filenames.Count == 0)
475 FileDependenciesArray.AddRange (filenames);
478 public void AddFileDependencies (string[] filenames)
480 if (filenames == null || filenames.Length == 0)
482 FileDependenciesArray.AddRange (filenames);
485 public void AddFileDependency (string filename)
487 if (filename == null || filename == String.Empty)
489 FileDependenciesArray.Add (filename);
492 public void AddHeader (string name, string value)
494 AppendHeader (name, value);
497 public void AppendCookie (HttpCookie cookie)
499 Cookies.Add (cookie);
504 // Special case for Content-Length, Content-Type, Transfer-Encoding and Cache-Control
507 public void AppendHeader (string name, string value)
510 throw new HttpException ("Headers have been already sent");
511 if (String.Compare (name, "content-length", StringComparison.OrdinalIgnoreCase) == 0){
512 content_length = (long) UInt64.Parse (value);
517 if (String.Compare (name, "content-type", StringComparison.OrdinalIgnoreCase) == 0){
522 if (String.Compare (name, "transfer-encoding", StringComparison.OrdinalIgnoreCase) == 0){
523 transfer_encoding = value;
528 if (String.Compare (name, "cache-control", StringComparison.OrdinalIgnoreCase) == 0){
529 user_cache_control = value;
533 Headers.Add (name, value);
536 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
537 public void AppendToLog (string param)
539 Console.Write ("System.Web: ");
540 Console.WriteLine (param);
543 public string ApplyAppPathModifier (string virtualPath)
545 if (virtualPath == null || context == null)
548 if (virtualPath.Length == 0)
549 return context.Request.RootVirtualDir;
551 if (UrlUtils.IsRelativeUrl (virtualPath)) {
552 virtualPath = UrlUtils.Combine (context.Request.RootVirtualDir, virtualPath);
553 } else if (UrlUtils.IsRooted (virtualPath)) {
554 virtualPath = UrlUtils.Canonic (virtualPath);
557 bool cookieless = false;
558 SessionStateSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/sessionState") as SessionStateSection;
559 cookieless = SessionStateModule.IsCookieLess (context, config);
564 if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) < 0) {
565 if (UrlUtils.HasSessionId (virtualPath))
566 virtualPath = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (virtualPath), virtualPath);
567 return UrlUtils.InsertSessionId (app_path_mod, virtualPath);
573 public void BinaryWrite (byte [] buffer)
575 output_stream.Write (buffer, 0, buffer.Length);
578 internal void BinaryWrite (byte [] buffer, int start, int len)
580 output_stream.Write (buffer, start, len);
588 public void ClearContent ()
590 output_stream.Clear ();
594 public void ClearHeaders ()
597 throw new HttpException ("headers have been already sent");
599 // Reset the special case headers.
601 content_type = "text/html";
602 transfer_encoding = null;
603 user_cache_control = "private";
604 if (cache_policy != null)
605 cache_policy.Cacheability = HttpCacheability.Private;
611 internal bool HeadersSent {
621 if (WorkerRequest != null)
622 WorkerRequest.CloseConnection ();
626 public void DisableKernelCache ()
628 // does nothing in Mono
636 if (context.TimeoutPossible) {
637 Thread.CurrentThread.Abort (FlagEnd.Value);
639 // If this is called from an async event, signal the completion
641 HttpApplication app_instance = context.ApplicationInstance;
642 if (app_instance != null)
643 app_instance.CompleteRequest ();
650 // Transfer-Encoding (chunked)
653 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);
662 if (redirect_location != null)
663 write_headers.Add ("Location", redirect_location);
665 string vh = VersionHeader;
667 write_headers.Add ("X-AspNet-Version", vh);
670 // If Content-Length is set.
672 if (content_length >= 0) {
673 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
674 content_length.ToString (Helpers.InvariantCulture));
675 } else if (BufferOutput) {
678 // If we are buffering and this is the last flush, not a middle-flush,
679 // we know the content-length.
681 content_length = output_stream.total;
682 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
683 content_length.ToString (Helpers.InvariantCulture));
686 // We are buffering, and this is a flush in the middle.
687 // If we are not chunked, we need to set "Connection: close".
690 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
695 // If the content-length is not set, and we are not buffering, we must
699 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
704 // Cache Control, the cache policy takes precedence over the cache_control property.
706 if (cache_policy != null)
707 cache_policy.SetHeaders (this, headers);
709 write_headers.Add ("Cache-Control", CacheControl);
714 if (content_type != null){
715 string header = content_type;
717 if (charset_set || header == "text/plain" || header == "text/html") {
718 if (header.IndexOf ("charset=") == -1 && !string.IsNullOrEmpty (charset)) {
719 header += "; charset=" + charset;
723 write_headers.Add ("Content-Type", header);
726 if (cookies != null && cookies.Count != 0){
727 int n = cookies.Count;
728 for (int i = 0; i < n; i++)
729 write_headers.Add ("Set-Cookie", cookies.Get (i).GetCookieHeaderValue ());
733 internal void WriteHeaders (bool final_flush)
741 if (context != null) {
742 HttpApplication app_instance = context.ApplicationInstance;
743 if (app_instance != null)
744 app_instance.TriggerPreSendRequestHeaders ();
749 if (cached_response != null)
750 cached_response.SetHeaders (headers);
752 // If this page is cached use the cached headers
753 // instead of the standard headers
754 NameValueCollection write_headers;
755 if (cached_headers != null)
756 write_headers = cached_headers;
758 write_headers = Headers;
759 AddHeadersNoCache (write_headers, final_flush);
762 if (WorkerRequest != null)
763 WorkerRequest.SendStatus (status_code, StatusDescription);
765 if (WorkerRequest != null) {
770 for (int i = 0; i < write_headers.Count; i++) {
771 header_name = write_headers.GetKey (i);
772 header_index = HttpWorkerRequest.GetKnownResponseHeaderIndex (header_name);
773 values = write_headers.GetValues (i);
777 foreach (string val in values) {
778 if (header_index > -1)
779 WorkerRequest.SendKnownResponseHeader (header_index, val);
781 WorkerRequest.SendUnknownResponseHeader (header_name, val);
787 internal void DoFilter (bool close)
789 if (output_stream.HaveFilter && context != null && context.Error == null)
790 output_stream.ApplyFilter (close);
793 internal void Flush (bool final_flush)
796 throw new HttpException ("Server cannot flush a completed response");
798 DoFilter (final_flush);
800 if (final_flush || status_code != 200)
804 bool head = ((context != null) && (context.Request.HttpMethod == "HEAD"));
805 if (suppress_content || head) {
808 output_stream.Clear ();
809 if (WorkerRequest != null)
810 output_stream.Flush (WorkerRequest, true); // ignore final_flush here.
814 completed = final_flush;
817 WriteHeaders (final_flush);
819 if (context != null) {
820 HttpApplication app_instance = context.ApplicationInstance;
821 if (app_instance != null)
822 app_instance.TriggerPreSendRequestContent ();
826 cached_response.SetData (output_stream.GetData ());
828 if (WorkerRequest != null)
829 output_stream.Flush (WorkerRequest, final_flush);
837 public void Pics (string value)
839 AppendHeader ("PICS-Label", value);
842 void Redirect (string url, bool endResponse, int code)
845 throw new ArgumentNullException ("url");
848 throw new HttpException ("Headers have already been sent");
850 if (url.IndexOf ('\n') != -1)
851 throw new ArgumentException ("Redirect URI cannot contain newline characters.", "url");
853 is_request_being_redirected = true;
858 url = ApplyAppPathModifier (url);
860 bool isFullyQualified;
861 if (StrUtils.StartsWith (url, "http:", true) ||
862 StrUtils.StartsWith (url, "https:", true) ||
863 StrUtils.StartsWith (url, "file:", true) ||
864 StrUtils.StartsWith (url, "ftp:", true))
865 isFullyQualified = true;
867 isFullyQualified = false;
869 if (!isFullyQualified) {
870 HttpRuntimeSection config = HttpRuntime.Section;
871 if (config != null && config.UseFullyQualifiedRedirectUrl) {
872 var ub = new UriBuilder (context.Request.Url);
873 int qpos = url.IndexOf ('?');
878 ub.Path = url.Substring (0, qpos);
879 ub.Query = url.Substring (qpos + 1);
884 url = ub.Uri.ToString ();
888 redirect_location = url;
890 // Text for browsers that can't handle location header
891 Write ("<html><head><title>Object moved</title></head><body>\r\n");
892 Write ("<h2>Object moved to <a href=\"" + url + "\">here</a></h2>\r\n");
893 Write ("</body><html>\r\n");
897 is_request_being_redirected = false;
900 public void Redirect (string url)
902 Redirect (url, true);
905 public void Redirect (string url, bool endResponse)
907 Redirect (url, endResponse, 302);
909 public void RedirectPermanent (string url)
911 RedirectPermanent (url, true);
914 public void RedirectPermanent (string url, bool endResponse)
916 Redirect (url, endResponse, 301);
919 public void RedirectToRoute (object routeValues)
921 RedirectToRoute ("RedirectToRoute", null, new RouteValueDictionary (routeValues), 302, true);
924 public void RedirectToRoute (RouteValueDictionary routeValues)
926 RedirectToRoute ("RedirectToRoute", null, routeValues, 302, true);
929 public void RedirectToRoute (string routeName)
931 RedirectToRoute ("RedirectToRoute", routeName, null, 302, true);
934 public void RedirectToRoute (string routeName, object routeValues)
936 RedirectToRoute ("RedirectToRoute", routeName, new RouteValueDictionary (routeValues), 302, true);
939 public void RedirectToRoute (string routeName, RouteValueDictionary routeValues)
941 RedirectToRoute ("RedirectToRoute", routeName, routeValues, 302, true);
944 public void RedirectToRoutePermanent (object routeValues)
946 RedirectToRoute ("RedirectToRoutePermanent", null, new RouteValueDictionary (routeValues), 301, false);
949 public void RedirectToRoutePermanent (RouteValueDictionary routeValues)
951 RedirectToRoute ("RedirectToRoutePermanent", null, routeValues, 301, false);
954 public void RedirectToRoutePermanent (string routeName)
956 RedirectToRoute ("RedirectToRoutePermanent", routeName, null, 301, false);
959 public void RedirectToRoutePermanent (string routeName, object routeValues)
961 RedirectToRoute ("RedirectToRoutePermanent", routeName, new RouteValueDictionary (routeValues), 301, false);
964 public void RedirectToRoutePermanent (string routeName, RouteValueDictionary routeValues)
966 RedirectToRoute ("RedirectToRoutePermanent", routeName, routeValues, 301, false);
969 void RedirectToRoute (string callerName, string routeName, RouteValueDictionary routeValues, int redirectCode, bool endResponse)
971 HttpContext ctx = context ?? HttpContext.Current;
972 HttpRequest req = ctx != null ? ctx.Request : null;
975 // Let's emulate .NET
976 throw new NullReferenceException ();
978 VirtualPathData vpd = RouteTable.Routes.GetVirtualPath (req.RequestContext, routeName, routeValues);
979 string redirectUrl = vpd != null ? vpd.VirtualPath : null;
980 if (String.IsNullOrEmpty (redirectUrl))
981 throw new InvalidOperationException ("No matching route found for RedirectToRoute");
983 Redirect (redirectUrl, true, redirectCode);
986 public static void RemoveOutputCacheItem (string path, string providerName)
989 throw new ArgumentNullException ("path");
991 if (path.Length > 0 && path [0] != '/')
992 throw new ArgumentException ("Invalid path for HttpResponse.RemoveOutputCacheItem: '" + path + "'. An absolute virtual path is expected");
994 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.");
1007 RemoveOutputCacheItem (path, OutputCache.DefaultProviderName);
1010 public void SetCookie (HttpCookie cookie)
1012 AppendCookie (cookie);
1015 public void Write (char ch)
1017 TextWriter writer = Output;
1020 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1024 public void Write (object obj)
1026 TextWriter writer = Output;
1029 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1033 writer.Write (obj.ToString ());
1036 public void Write (string s)
1038 TextWriter writer = Output;
1041 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1045 public void Write (char [] buffer, int index, int count)
1047 TextWriter writer = Output;
1050 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1051 writer.Write (buffer, index, count);
1054 bool IsFileSystemDirSeparator (char ch)
1056 return ch == '\\' || ch == '/';
1059 string GetNormalizedFileName (string fn)
1061 if (String.IsNullOrEmpty (fn))
1064 // On Linux we don't change \ to / since filenames with \ are valid. We also
1065 // don't remove drive: designator for the same reason.
1066 int len = fn.Length;
1067 if (len >= 3 && fn [1] == ':' && IsFileSystemDirSeparator (fn [2]))
1068 return Path.GetFullPath (fn); // drive-qualified absolute file path
1070 bool startsWithDirSeparator = IsFileSystemDirSeparator (fn [0]);
1071 if (len >= 2 && startsWithDirSeparator && IsFileSystemDirSeparator (fn [1]))
1072 return Path.GetFullPath (fn); // UNC path
1074 if (!startsWithDirSeparator) {
1075 HttpContext ctx = context ?? HttpContext.Current;
1076 HttpRequest req = ctx != null ? ctx.Request : null;
1079 return req.MapPath (fn);
1082 return fn; // Or should we rather throw?
1085 internal void WriteFile (FileStream fs, long offset, long size)
1087 byte [] buffer = new byte [32*1024];
1090 fs.Position = offset;
1094 while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
1096 output_stream.Write (buffer, 0, n);
1100 public void WriteFile (string filename)
1102 WriteFile (filename, false);
1105 public void WriteFile (string filename, bool readIntoMemory)
1107 if (filename == null)
1108 throw new ArgumentNullException ("filename");
1110 string fn = GetNormalizedFileName (filename);
1111 if (readIntoMemory){
1112 using (FileStream fs = File.OpenRead (fn))
1113 WriteFile (fs, 0, fs.Length);
1115 FileInfo fi = new FileInfo (fn);
1116 output_stream.WriteFile (fn, 0, fi.Length);
1121 output_stream.ApplyFilter (false);
1125 public void WriteFile (IntPtr fileHandle, long offset, long size)
1128 throw new ArgumentNullException ("offset can not be negative");
1130 throw new ArgumentNullException ("size can not be negative");
1135 // Note: this .ctor will throw a SecurityException if the caller
1136 // doesn't have the UnmanagedCode permission
1137 using (FileStream fs = new FileStream (fileHandle, FileAccess.Read))
1138 WriteFile (fs, offset, size);
1142 output_stream.ApplyFilter (false);
1146 public void WriteFile (string filename, long offset, long size)
1148 if (filename == null)
1149 throw new ArgumentNullException ("filename");
1151 throw new ArgumentNullException ("offset can not be negative");
1153 throw new ArgumentNullException ("size can not be negative");
1158 FileStream fs = File.OpenRead (filename);
1159 WriteFile (fs, offset, size);
1164 output_stream.ApplyFilter (false);
1168 public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
1170 // Emulation of .NET behavior
1171 if (callback == null)
1172 throw new NullReferenceException ();
1174 object target = callback.Target;
1175 if (target != null && target.GetType () == typeof (Control))
1176 throw new ArgumentException ("callback");
1178 string s = callback (context);
1184 Cache.Cacheability = HttpCacheability.Server;
1186 if (WorkerRequest == null)
1187 Write (s); // better this than nothing
1189 byte[] bytes = WebEncoding.ResponseEncoding.GetBytes (s);
1190 WorkerRequest.SendResponseFromMemory (bytes, bytes.Length);
1193 cached_response.SetData (callback);
1197 // Like WriteFile, but never buffers, so we manually Flush here
1199 public void TransmitFile (string filename)
1201 if (filename == null)
1202 throw new ArgumentNullException ("filename");
1204 TransmitFile (filename, false);
1207 internal void TransmitFile (string filename, bool final_flush)
1209 FileInfo fi = new FileInfo (filename);
1210 using (Stream s = fi.OpenRead ()) { } // Just check if we can read.
1211 output_stream.WriteFile (filename, 0, fi.Length);
1212 output_stream.ApplyFilter (final_flush);
1213 Flush (final_flush);
1216 public void TransmitFile (string filename, long offset, long length)
1218 output_stream.WriteFile (filename, offset, length);
1219 output_stream.ApplyFilter (false);
1223 internal void TransmitFile (VirtualFile vf)
1225 TransmitFile (vf, false);
1228 const int bufLen = 65535;
1229 internal void TransmitFile (VirtualFile vf, bool final_flush)
1232 throw new ArgumentNullException ("vf");
1234 if (vf is DefaultVirtualFile) {
1235 TransmitFile (HostingEnvironment.MapPath (vf.VirtualPath), final_flush);
1239 byte[] buf = new byte [bufLen];
1240 using (Stream s = vf.Open ()) {
1242 while ((readBytes = s.Read (buf, 0, bufLen)) > 0) {
1243 output_stream.Write (buf, 0, readBytes);
1244 output_stream.ApplyFilter (final_flush);
1252 #region Session state support
1253 internal void SetAppPathModifier (string app_modifier)
1255 app_path_mod = app_modifier;
1259 #region Cache Support
1260 internal void SetCachedHeaders (NameValueCollection headers)
1262 cached_headers = headers;
1266 internal bool IsCached {
1267 get { return cached_response != null; }
1270 cached_response = new CachedRawResponse (cache_policy);
1272 cached_response = null;
1276 public HttpCachePolicy Cache {
1278 if (cache_policy == null)
1279 cache_policy = new HttpCachePolicy ();
1281 return cache_policy;
1285 internal CachedRawResponse GetCachedResponse ()
1287 if (cached_response != null) {
1288 cached_response.StatusCode = StatusCode;
1289 cached_response.StatusDescription = StatusDescription;
1292 return cached_response;
1296 // This is one of the old ASP compatibility methods, the real cache
1297 // control is in the Cache property, and this is a second class citizen
1299 public string CacheControl {
1301 if (value == null || value == "") {
1302 Cache.SetCacheability (HttpCacheability.NoCache);
1303 user_cache_control = null;
1304 } else if (String.Compare (value, "public", true, Helpers.InvariantCulture) == 0) {
1305 Cache.SetCacheability (HttpCacheability.Public);
1306 user_cache_control = "public";
1307 } else if (String.Compare (value, "private", true, Helpers.InvariantCulture) == 0) {
1308 Cache.SetCacheability (HttpCacheability.Private);
1309 user_cache_control = "private";
1310 } else if (String.Compare (value, "no-cache", true, Helpers.InvariantCulture) == 0) {
1311 Cache.SetCacheability (HttpCacheability.NoCache);
1312 user_cache_control = "no-cache";
1314 throw new ArgumentException ("CacheControl property only allows `public', " +
1315 "`private' or no-cache, for different uses, use " +
1316 "Response.AppendHeader");
1319 get { return (user_cache_control != null) ? user_cache_control : "private"; }
1323 internal int GetOutputByteCount ()
1325 return output_stream.GetTotalLength ();
1328 internal void ReleaseResources ()
1330 if (output_stream != null)
1331 output_stream.ReleaseResources (true);
1340 static class FlagEnd
1342 public static readonly object Value = new object ();