5895e7044a6199aa475e505289e298f6f96f27ea
[mono.git] / mcs / class / System.Web / System.Web / HttpResponse.cs
1 //
2 // System.Web.HttpResponse.cs 
3 //
4 // 
5 // Author:
6 //      Miguel de Icaza (miguel@novell.com)
7 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 //
9 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System.Text;
32 using System.Web.UI;
33 using System.Collections;
34 using System.Collections.Specialized;
35 using System.IO;
36 using System.Web.Caching;
37 using System.Threading;
38 using System.Web.Util;
39 using System.Web.Configuration;
40 using System.Globalization;
41 using System.Security.Permissions;
42 using System.Web.Hosting;
43 using System.Web.SessionState;
44
45 namespace System.Web {
46         
47         // CAS - no InheritanceDemand here as the class is sealed
48         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
49         public sealed partial class HttpResponse {
50                 internal HttpWorkerRequest WorkerRequest;
51                 internal HttpResponseStream output_stream;
52                 internal bool buffer = true;
53
54                 ArrayList fileDependencies;
55                 
56                 HttpContext context;
57                 TextWriter writer;
58                 HttpCachePolicy cache_policy;
59                 Encoding encoding;
60                 HttpCookieCollection cookies;
61                 
62                 int status_code = 200;
63                 string status_description = "OK";
64
65                 string content_type = "text/html";
66                 string charset;
67                 bool charset_set;
68                 CachedRawResponse cached_response;
69                 string user_cache_control = "private";
70                 string redirect_location;
71                 
72                 static string version_header;
73                 
74                 //
75                 // Negative Content-Length means we auto-compute the size of content-length
76                 // can be overwritten with AppendHeader ("Content-Length", value)
77                 //
78                 long content_length = -1;
79
80                 //
81                 // The list of the headers that we will send back to the client, except
82                 // the headers that we compute here.
83                 //
84
85                 NameValueCollection headers;
86                 bool headers_sent;
87                 NameValueCollection cached_headers;
88
89                 //
90                 // Transfer encoding state
91                 //
92                 string transfer_encoding;
93                 internal bool use_chunked;
94                 
95                 bool closed;
96                 internal bool suppress_content;
97
98                 //
99                 // Session State
100                 //
101                 string app_path_mod;
102                 
103 #if NET_2_0
104                 bool is_request_being_redirected;
105                 Encoding headerEncoding;
106 #endif
107
108                 static HttpResponse ()
109                 {
110 #if NET_2_0
111                         HttpRuntimeSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/httpRuntime") as HttpRuntimeSection;
112 #else
113                         HttpRuntimeConfig config = HttpContext.GetAppConfig ("system.web/httpRuntime") as HttpRuntimeConfig;
114 #endif
115                         if (config != null && config.EnableVersionHeader)
116                                 version_header = Environment.Version.ToString (3);
117                 }
118                 
119                 internal HttpResponse ()
120                 {
121                         output_stream = new HttpResponseStream (this);
122                 }
123
124                 public HttpResponse (TextWriter writer) : this ()
125                 {
126                         this.writer = writer;
127                 }
128
129                 internal HttpResponse (HttpWorkerRequest worker_request, HttpContext context) : this ()
130                 {
131                         WorkerRequest = worker_request;
132                         this.context = context;
133
134 #if !TARGET_J2EE
135                         if (worker_request != null)
136                                 use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1");
137 #endif
138                 }
139                 
140                 internal TextWriter SetTextWriter (TextWriter writer)
141                 {
142                         TextWriter prev = this.writer;
143                         this.writer = writer;
144                         return prev;
145                 }
146
147                 internal string[] FileDependencies {
148                         get {
149                                 if (fileDependencies == null || fileDependencies.Count == 0)
150                                         return new string[0] {};
151                                 return (string[]) fileDependencies.ToArray (typeof (string));
152                         }
153                 }
154                 
155                 ArrayList FileDependenciesArray {
156                         get {
157                                 if (fileDependencies == null)
158                                         fileDependencies = new ArrayList ();
159                                 return fileDependencies;
160                         }
161                 }
162                 
163                 public bool Buffer {
164                         get {
165                                 return buffer;
166                         }
167
168                         set {
169                                 buffer = value;
170                         }
171                 }
172
173                 public bool BufferOutput {
174                         get {
175                                 return buffer;
176                         }
177
178                         set {
179                                 buffer = value;
180                         }
181                 }
182
183                 //
184                 // Use the default from <globalization> section if the client has not set the encoding
185                 //
186                 public Encoding ContentEncoding {
187                         get {
188                                 if (encoding == null) {
189                                         if (context != null) {
190                                                 string client_content_type = context.Request.ContentType;
191                                                 string parameter = HttpRequest.GetParameter (client_content_type, "; charset=");
192                                                 if (parameter != null) {
193                                                         try {
194                                                                 // Do what the #1 web server does
195                                                                 encoding = Encoding.GetEncoding (parameter);
196                                                         } catch {
197                                                         }
198                                                 }
199                                         }
200                                         if (encoding == null)
201                                                 encoding = WebEncoding.ResponseEncoding;
202                                 }
203                                 return encoding;
204                         }
205
206                         set {
207                                 if (value == null)
208                                         throw new ArgumentException ("ContentEncoding can not be null");
209
210                                 encoding = value;
211                                 HttpWriter http_writer = writer as HttpWriter;
212                                 if (http_writer != null)
213                                         http_writer.SetEncoding (encoding);
214                         }
215                 }
216                 
217                 public string ContentType {
218                         get {
219                                 return content_type;
220                         }
221
222                         set {
223                                 content_type = value;
224                         }
225                 }
226
227                 public string Charset {
228                         get {
229                                 if (charset == null)
230                                         charset = ContentEncoding.WebName;
231                                 
232                                 return charset;
233                         }
234
235                         set {
236                                 charset_set = true;
237                                 charset = value;
238                         }
239                 }
240                 
241                 public HttpCookieCollection Cookies {
242                         get {
243                                 if (cookies == null)
244                                         cookies = new HttpCookieCollection (true, false);
245                                 return cookies;
246                         }
247                 }
248                 
249                 public int Expires {
250                         get {
251                                 if (cache_policy == null)
252                                         return 0;
253
254                                 return cache_policy.ExpireMinutes ();
255                         }
256
257                         set {
258                                 Cache.SetExpires (DateTime.Now + new TimeSpan (0, value, 0));
259                         }
260                 }
261                 
262                 public DateTime ExpiresAbsolute {
263                         get {
264                                 return Cache.Expires;
265                         }
266
267                         set {
268                                 Cache.SetExpires (value);
269                         }
270                 }
271
272                 public Stream Filter {
273                         get {
274                                 if (WorkerRequest == null)
275                                         return null;
276
277                                 return output_stream.Filter;
278                         }
279
280                         set {
281                                 output_stream.Filter = value;
282                         }
283                 }
284 #if NET_2_0
285                 public Encoding HeaderEncoding {
286                         get {
287                                 if (headerEncoding == null) {
288                                         GlobalizationSection gs = WebConfigurationManager.SafeGetSection ("system.web/globalization", typeof (GlobalizationSection)) as GlobalizationSection;
289
290                                         if (gs == null)
291                                                 headerEncoding = Encoding.UTF8;
292                                         else {
293                                                 headerEncoding = gs.ResponseHeaderEncoding;
294                                                 if (headerEncoding == Encoding.Unicode)
295                                                         throw new HttpException ("HeaderEncoding must not be Unicode");
296                                         }
297                                 }
298                                 return headerEncoding;
299                         }
300                         set {
301                                 if (headers_sent)
302                                         throw new HttpException ("headers have already been sent");
303                                 if (value == null)
304                                         throw new ArgumentNullException ("HeaderEncoding");
305                                 if (value == Encoding.Unicode)
306                                         throw new HttpException ("HeaderEncoding must not be Unicode");
307                                 headerEncoding = value;
308                         }
309                 }
310
311                 public
312 #else
313                 internal
314 #endif
315                 NameValueCollection Headers {
316                         get {
317                                 if (headers == null)
318                                         headers = new NameValueCollection ();
319
320                                 return headers;
321                         }
322                 }
323
324                 
325                 public bool IsClientConnected {
326                         get {
327                                 if (WorkerRequest == null)
328                                         return true; // yep that's true
329
330                                 return WorkerRequest.IsClientConnected ();
331                         }
332                 }
333 #if NET_2_0
334                 public bool IsRequestBeingRedirected {
335                         get { return is_request_being_redirected; }
336                 }
337 #endif
338                 public TextWriter Output {
339                         get {
340                                 if (writer == null)
341                                         writer = new HttpWriter (this);
342
343                                 return writer;
344                         }
345                 }
346
347                 public Stream OutputStream {
348                         get {
349                                 return output_stream;
350                         }
351                 }
352                 
353                 public string RedirectLocation {
354                         get {
355                                 return redirect_location;
356                         }
357
358                         set {
359                                 redirect_location = value;
360                         }
361                 }
362                 
363                 public string Status {
364                         get { return String.Concat (status_code.ToString (), " ", StatusDescription); }
365
366                         set {
367                                 int p = value.IndexOf (' ');
368                                 if (p == -1)
369                                         throw new HttpException ("Invalid format for the Status property");
370
371                                 string s = value.Substring (0, p);
372                                 
373 #if NET_2_0
374                                 if (!Int32.TryParse (s, out status_code))
375                                         throw new HttpException ("Invalid format for the Status property");
376 #else
377                                                     
378                                 try {
379                                         status_code = Int32.Parse (s);
380                                 } catch {
381                                         throw new HttpException ("Invalid format for the Status property");
382                                 }
383 #endif
384                                 
385                                 status_description = value.Substring (p+1);
386                         }
387                 }
388
389 #if NET_2_0
390                 // We ignore the two properties on Mono as they are for use with IIS7, but there is
391                 // no point in throwing PlatformNotSupportedException. We might find a use for them
392                 // some day.
393                 public int SubStatusCode {
394                         get;
395                         set;
396                 }
397
398                 public bool TrySkipIisCustomErrors {
399                         get;
400                         set;
401                 }
402 #endif
403                 
404                 public int StatusCode {
405                         get {
406                                 return status_code;
407                         }
408
409                         set {
410                                 if (headers_sent)
411                                         throw new HttpException ("headers have already been sent");
412                                 
413                                 status_code = value;
414                                 status_description = null;
415                         }
416                 }
417
418                 public string StatusDescription {
419                         get {
420                                 if (status_description == null)
421                                         status_description = HttpWorkerRequest.GetStatusDescription (status_code);
422
423                                 return status_description;
424                         }
425
426                         set {
427                                 if (headers_sent)
428                                         throw new HttpException ("headers have already been sent");
429                                 
430                                 status_description = value;
431                         }
432                 }
433                 
434                 public bool SuppressContent {
435                         get {
436                                 return suppress_content;
437                         }
438
439                         set {
440                                 suppress_content = value;
441                         }
442                 }
443
444 #if NET_2_0
445                 [MonoTODO ("Not implemented")]
446                 public void AddCacheDependency (CacheDependency[] dependencies)
447                 {
448                         throw new NotImplementedException ();
449                 }
450
451                 [MonoTODO ("Not implemented")]
452                 public void AddCacheItemDependencies (string[] cacheKeys)
453                 {
454                         throw new NotImplementedException ();
455                 }
456 #endif
457                 [MonoTODO("Currently does nothing")]
458                 public void AddCacheItemDependencies (ArrayList cacheKeys)
459                 {
460                         // TODO: talk to jackson about the cache
461                 }
462
463                 [MonoTODO("Currently does nothing")]
464                 public void AddCacheItemDependency (string cacheKey)
465                 {
466                         // TODO: talk to jackson about the cache
467                 }
468                 
469                 public void AddFileDependencies (ArrayList filenames)
470                 {
471                         if (filenames == null || filenames.Count == 0)
472                                 return;
473                         FileDependenciesArray.AddRange (filenames);
474                 }
475 #if NET_2_0
476                 public void AddFileDependencies (string[] filenames)
477                 {
478                         if (filenames == null || filenames.Length == 0)
479                                 return;
480                         FileDependenciesArray.AddRange (filenames);
481                 }
482 #endif          
483
484                 public void AddFileDependency (string filename)
485                 {
486                         if (filename == null || filename == String.Empty)
487                                 return;
488                         FileDependenciesArray.Add (filename);
489                 }
490
491                 public void AddHeader (string name, string value)
492                 {
493                         AppendHeader (name, value);
494                 }
495
496                 public void AppendCookie (HttpCookie cookie)
497                 {
498                         Cookies.Add (cookie);
499                 }
500
501                 //
502                 // AppendHeader:
503                 //    Special case for Content-Length, Content-Type, Transfer-Encoding and Cache-Control
504                 //
505                 //
506                 public void AppendHeader (string name, string value)
507                 {
508                         if (headers_sent)
509                                 throw new HttpException ("headers have been already sent");
510                         
511 #if !TARGET_J2EE
512                         if (String.Compare (name, "content-length", true, CultureInfo.InvariantCulture) == 0){
513                                 content_length = (long) UInt64.Parse (value);
514                                 use_chunked = false;
515                                 return;
516                         }
517 #endif
518
519                         if (String.Compare (name, "content-type", true, CultureInfo.InvariantCulture) == 0){
520                                 ContentType = value;
521                                 return;
522                         }
523
524 #if !TARGET_J2EE
525                         if (String.Compare (name, "transfer-encoding", true, CultureInfo.InvariantCulture) == 0){
526                                 transfer_encoding = value;
527                                 use_chunked = false;
528                                 return;
529                         }
530 #endif
531
532                         if (String.Compare (name, "cache-control", true, CultureInfo.InvariantCulture) == 0){
533                                 user_cache_control = value;
534                                 return;
535                         }
536
537                         Headers.Add (name, value);
538                 }
539
540                 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
541                 public void AppendToLog (string param)
542                 {
543                         Console.Write ("System.Web: ");
544                         Console.WriteLine (param);
545                 }
546                 
547                 public string ApplyAppPathModifier (string virtualPath)
548                 {
549                         if (virtualPath == null)
550                                 return null;
551                 
552                         if (virtualPath.Length == 0)
553                                 return context.Request.RootVirtualDir;
554
555                         if (UrlUtils.IsRelativeUrl (virtualPath)) {
556                                 virtualPath = UrlUtils.Combine (context.Request.RootVirtualDir, virtualPath);
557                         } else if (UrlUtils.IsRooted (virtualPath)) {
558                                 virtualPath = UrlUtils.Canonic (virtualPath);
559                         }
560
561                         bool cookieless = false;
562 #if NET_2_0
563                         SessionStateSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/sessionState") as SessionStateSection;
564                         cookieless = SessionStateModule.IsCookieLess (context, config);
565 #else
566                         SessionConfig config = HttpContext.GetAppConfig ("system.web/sessionState") as SessionConfig;
567                         cookieless = config.CookieLess;
568 #endif
569                         if (!cookieless)
570                                 return virtualPath;
571
572                         if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) < 0) {
573                                 if (UrlUtils.HasSessionId (virtualPath))
574                                         virtualPath = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (virtualPath), virtualPath);
575                                 return UrlUtils.InsertSessionId (app_path_mod, virtualPath);
576                         }
577                 
578                         return virtualPath;
579                 }
580
581                 public void BinaryWrite (byte [] buffer)
582                 {
583                         output_stream.Write (buffer, 0, buffer.Length);
584                 }
585
586                 internal void BinaryWrite (byte [] buffer, int start, int len)
587                 {
588                         output_stream.Write (buffer, start, len);
589                 }
590
591                 public void Clear ()
592                 {
593                         ClearContent ();
594                 }
595
596                 public void ClearContent ()
597                 {
598                         output_stream.Clear ();
599                         content_length = -1;
600                 }
601
602                 public void ClearHeaders ()
603                 {
604                         if (headers_sent)
605                                 throw new HttpException ("headers have been already sent");
606
607                         // Reset the special case headers.
608                         content_length = -1;
609                         content_type = "text/html";
610                         transfer_encoding = null;
611                         user_cache_control = "private";
612                         if (cache_policy != null)
613                                 cache_policy.Cacheability = HttpCacheability.Private;
614
615                         if (headers != null)
616                                 headers.Clear ();
617                 }
618
619                 internal bool HeadersSent {
620                         get {
621                                 return headers_sent;
622                         }
623                 }
624
625                 public void Close ()
626                 {
627                         if (closed)
628                                 return;
629                         if (WorkerRequest != null)
630                                 WorkerRequest.CloseConnection ();
631                         closed = true;
632                 }
633
634 #if NET_2_0
635                 public void DisableKernelCache ()
636                 {
637                         // does nothing in Mono
638                 }
639 #endif
640                 
641                 public void End ()
642                 {
643                         if (context == null)
644                                 return;
645                         
646                         if (context.TimeoutPossible) {
647                                 Thread.CurrentThread.Abort (FlagEnd.Value);
648                         } else {
649                                 // If this is called from an async event, signal the completion
650                                 // but don't throw.
651                                 HttpApplication app_instance = context.ApplicationInstance;
652                                 if (app_instance != null)
653                                         app_instance.CompleteRequest ();
654                         }
655                 }
656
657                 // Generate:
658                 //   Content-Length
659                 //   Content-Type
660                 //   Transfer-Encoding (chunked)
661                 //   Cache-Control
662                 //   X-AspNet-Version
663                 void AddHeadersNoCache (NameValueCollection write_headers, bool final_flush)
664                 {
665 #if !TARGET_J2EE
666                         //
667                         // Transfer-Encoding
668                         //
669                         if (use_chunked)
670                                 write_headers.Add ("Transfer-Encoding", "chunked");
671                         else if (transfer_encoding != null)
672                                 write_headers.Add ("Transfer-Encoding", transfer_encoding);
673 #endif
674                         if (redirect_location != null)
675                                 write_headers.Add ("Location", redirect_location);
676                         
677 #if !TARGET_J2EE
678                         if (version_header != null)
679                                 write_headers.Add ("X-AspNet-Version", version_header);
680
681                         //
682                         // If Content-Length is set.
683                         //
684                         if (content_length >= 0) {
685                                 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
686                                                    content_length.ToString (CultureInfo.InvariantCulture));
687                         } else if (BufferOutput) {
688                                 if (final_flush) {                                      
689                                         //
690                                         // If we are buffering and this is the last flush, not a middle-flush,
691                                         // we know the content-length.
692                                         //
693                                         content_length = output_stream.total;
694                                         write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
695                                                            content_length.ToString (CultureInfo.InvariantCulture));
696                                 } else {
697                                         //
698                                         // We are buffering, and this is a flush in the middle.
699                                         // If we are not chunked, we need to set "Connection: close".
700                                         //
701                                         if (use_chunked){
702                                                 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
703                                         }
704                                 }
705                         } else {
706                                 //
707                                 // If the content-length is not set, and we are not buffering, we must
708                                 // close at the end.
709                                 //
710                                 if (use_chunked){
711                                         write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
712                                 }
713                         }
714 #endif
715
716                         //
717                         // Cache Control, the cache policy takes precedence over the cache_control property.
718                         //
719                         if (cache_policy != null)
720                                 cache_policy.SetHeaders (this, headers);
721                         else
722                                 write_headers.Add ("Cache-Control", CacheControl);
723                         
724                         //
725                         // Content-Type
726                         //
727                         if (content_type != null){
728                                 string header = content_type;
729
730                                 if (charset_set || header == "text/plain" || header == "text/html") {
731                                         if (header.IndexOf ("charset=") == -1) {
732                                                 if (charset == null || charset == "")
733                                                         charset = ContentEncoding.HeaderName;
734                                                 header += "; charset=" + charset;
735                                         }
736                                 }
737                                 
738                                 write_headers.Add ("Content-Type", header);
739                         }
740
741                         if (cookies != null && cookies.Count != 0){
742                                 int n = cookies.Count;
743                                 for (int i = 0; i < n; i++)
744                                         write_headers.Add ("Set-Cookie", cookies.Get (i).GetCookieHeaderValue ());
745 #if TARGET_J2EE
746                                 // For J2EE Portal support emulate cookies by storing them in the session.
747                                 context.Request.SetSessionCookiesForPortal (cookies);
748 #endif
749                         }
750                 }
751
752                 internal void WriteHeaders (bool final_flush)
753                 {
754                         if (headers_sent)
755                                 return;
756
757                         //
758                         // Flush
759                         //
760                         if (context != null) {
761                                 HttpApplication app_instance = context.ApplicationInstance;
762                                 if (app_instance != null)
763                                         app_instance.TriggerPreSendRequestHeaders ();
764                         }
765
766                         headers_sent = true;
767
768                         if (cached_response != null)
769                                 cached_response.SetHeaders (headers);
770
771                         // If this page is cached use the cached headers
772                         // instead of the standard headers      
773                         NameValueCollection write_headers;
774                         if (cached_headers != null)
775                                 write_headers = cached_headers;
776                         else {
777                                 write_headers = Headers;
778                                 AddHeadersNoCache (write_headers, final_flush);
779                         }
780                         
781                         if (WorkerRequest != null)
782                                 WorkerRequest.SendStatus (status_code, StatusDescription);
783
784                         if (WorkerRequest != null) {
785                                 string header_name;
786                                 string[] values;
787                                 int header_index;
788                                 
789                                 for (int i = 0; i < write_headers.Count; i++) {
790                                         header_name = write_headers.GetKey (i);
791                                         header_index = HttpWorkerRequest.GetKnownResponseHeaderIndex (header_name);
792                                         values = write_headers.GetValues (i);
793                                         if (values == null)
794                                                 continue;
795                                         
796                                         foreach (string val in values) {
797                                                 if (header_index > -1)
798                                                         WorkerRequest.SendKnownResponseHeader (header_index, val);
799                                                 else
800                                                         WorkerRequest.SendUnknownResponseHeader (header_name, val);
801                                         }
802                                 }
803                         }
804                 }
805
806                 internal void DoFilter (bool close)
807                 {
808                         if (output_stream.HaveFilter && context != null && context.Error == null)
809                                 output_stream.ApplyFilter (close);
810                 }
811
812                 internal void Flush (bool final_flush)
813                 {
814                         DoFilter (final_flush);
815                         if (!headers_sent){
816                                 if (final_flush || status_code != 200)
817                                         use_chunked = false;
818                         }
819
820                         bool head = ((context != null) && (context.Request.HttpMethod == "HEAD"));
821                         if (suppress_content || head) {
822                                 if (!headers_sent)
823                                         WriteHeaders (true);
824                                 output_stream.Clear ();
825                                 if (WorkerRequest != null)
826                                         output_stream.Flush (WorkerRequest, true); // ignore final_flush here.
827                                 return;
828                         }
829
830                         if (!headers_sent)
831                                 WriteHeaders (final_flush);
832
833                         if (context != null) {
834                                 HttpApplication app_instance = context.ApplicationInstance;
835                                 if (app_instance != null)
836                                         app_instance.TriggerPreSendRequestContent ();
837                         }
838
839                         if (IsCached) {
840                                 MemoryStream ms = output_stream.GetData ();
841                                 cached_response.ContentLength = (int) ms.Length;
842                                 cached_response.SetData (ms.GetBuffer ());
843                         }
844
845                         if (WorkerRequest != null)
846                                 output_stream.Flush (WorkerRequest, final_flush);
847                 }
848
849                 public void Flush ()
850                 {
851                         Flush (false);
852                 }
853
854                 public void Pics (string value)
855                 {
856                         AppendHeader ("PICS-Label", value);
857                 }
858
859                 public void Redirect (string url)
860                 {
861                         Redirect (url, true);
862                 }
863
864                 public void Redirect (string url, bool endResponse)
865                 {
866                         if (headers_sent)
867                                 throw new HttpException ("Headers have already been sent");
868
869 #if NET_2_0
870                         is_request_being_redirected = true;
871 #endif
872                         ClearHeaders ();
873                         ClearContent ();
874                         
875                         StatusCode = 302;
876                         url = ApplyAppPathModifier (url);
877                         redirect_location = url;
878
879                         // Text for browsers that can't handle location header
880                         Write ("<html><head><title>Object moved</title></head><body>\r\n");
881                         Write ("<h2>Object moved to <a href=\"" + url + "\">here</a></h2>\r\n");
882                         Write ("</body><html>\r\n");
883                         
884                         if (endResponse)
885                                 End ();
886 #if NET_2_0
887                         is_request_being_redirected = false;
888 #endif
889                 }
890
891                 public static void RemoveOutputCacheItem (string path)
892                 {
893                         if (path == null)
894                                 throw new ArgumentNullException ("path");
895
896                         if (path.Length == 0)
897                                 return;
898
899                         if (path [0] != '/')
900                                 throw new ArgumentException ("'" + path + "' is not an absolute virtual path.");
901
902                         HttpRuntime.InternalCache.Remove (path);
903                 }
904
905                 public void SetCookie (HttpCookie cookie)
906                 {
907                         AppendCookie (cookie);
908                 }
909
910                 public void Write (char ch)
911                 {
912                         Output.Write (ch);
913                 }
914
915                 public void Write (object obj)
916                 {
917                         if (obj == null)
918                                 return;
919                         
920                         Output.Write (obj.ToString ());
921                 }
922                 
923                 public void Write (string s)
924                 {
925                         Output.Write (s);
926                 }
927                 
928                 public void Write (char [] buffer, int index, int count)
929                 {
930                         Output.Write (buffer, index, count);
931                 }
932
933                 internal void WriteFile (FileStream fs, long offset, long size)
934                 {
935                         byte [] buffer = new byte [32*1024];
936
937                         if (offset != 0)
938                                 fs.Position = offset;
939
940                         long remain = size;
941                         int n;
942                         while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
943                                 remain -= n;
944                                 output_stream.Write (buffer, 0, n);
945                         }
946                 }
947                 
948                 public void WriteFile (string filename)
949                 {
950                         WriteFile (filename, false);
951                 }
952
953                 public void WriteFile (string filename, bool readIntoMemory)
954                 {
955                         if (filename == null)
956                                 throw new ArgumentNullException ("filename");
957
958                         if (readIntoMemory){
959                                 using (FileStream fs = File.OpenRead (filename))
960                                         WriteFile (fs, 0, fs.Length);
961                         } else {
962                                 FileInfo fi = new FileInfo (filename);
963                                 output_stream.WriteFile (filename, 0, fi.Length);
964                         }
965                         if (buffer)
966                                 return;
967
968                         output_stream.ApplyFilter (false);
969                         Flush ();
970                 }
971
972 #if TARGET_JVM
973                 public void WriteFile (IntPtr fileHandle, long offset, long size) {
974                         throw new PlatformNotSupportedException("IntPtr not supported");
975                 }
976 #else
977                 public void WriteFile (IntPtr fileHandle, long offset, long size)
978                 {
979                         if (offset < 0)
980                                 throw new ArgumentNullException ("offset can not be negative");
981                         if (size < 0)
982                                 throw new ArgumentNullException ("size can not be negative");
983
984                         if (size == 0)
985                                 return;
986
987                         // Note: this .ctor will throw a SecurityException if the caller 
988                         // doesn't have the UnmanagedCode permission
989                         using (FileStream fs = new FileStream (fileHandle, FileAccess.Read))
990                                 WriteFile (fs, offset, size);
991
992                         if (buffer)
993                                 return;
994                         output_stream.ApplyFilter (false);
995                         Flush ();
996                 }
997 #endif
998
999                 public void WriteFile (string filename, long offset, long size)
1000                 {
1001                         if (filename == null)
1002                                 throw new ArgumentNullException ("filename");
1003                         if (offset < 0)
1004                                 throw new ArgumentNullException ("offset can not be negative");
1005                         if (size < 0)
1006                                 throw new ArgumentNullException ("size can not be negative");
1007
1008                         if (size == 0)
1009                                 return;
1010                         
1011                         FileStream fs = File.OpenRead (filename);
1012                         WriteFile (fs, offset, size);
1013
1014                         if (buffer)
1015                                 return;
1016
1017                         output_stream.ApplyFilter (false);
1018                         Flush ();
1019                 }
1020 #if NET_2_0
1021                 [MonoTODO ("Not implemented")]
1022                 public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
1023                 {
1024                         throw new NotImplementedException ();
1025                 }
1026 #endif
1027                 //
1028                 // Like WriteFile, but never buffers, so we manually Flush here
1029                 //
1030                 public void TransmitFile (string filename) 
1031                 {
1032                         if (filename == null)
1033                                 throw new ArgumentNullException ("filename");
1034
1035                         TransmitFile (filename, false);
1036                 }
1037
1038                 internal void TransmitFile (string filename, bool final_flush)
1039                 {
1040                         FileInfo fi = new FileInfo (filename);
1041                         using (Stream s = fi.OpenRead ()); // Just check if we can read.
1042                         output_stream.WriteFile (filename, 0, fi.Length);
1043                         output_stream.ApplyFilter (final_flush);
1044                         Flush (final_flush);
1045                 }
1046
1047 #if NET_2_0
1048                 public void TransmitFile (string filename, long offset, long length)
1049                 {
1050                         output_stream.WriteFile (filename, offset, length);
1051                         output_stream.ApplyFilter (false);
1052                         Flush (false);
1053                 }
1054                 
1055                 internal void TransmitFile (VirtualFile vf)
1056                 {
1057                         TransmitFile (vf, false);
1058                 }
1059
1060                 const int bufLen = 65535;
1061                 internal void TransmitFile (VirtualFile vf, bool final_flush)
1062                 {
1063                         if (vf == null)
1064                                 throw new ArgumentNullException ("vf");
1065
1066                         if (vf is DefaultVirtualFile) {
1067                                 TransmitFile (HostingEnvironment.MapPath (vf.VirtualPath), final_flush);
1068                                 return;
1069                         }
1070                         
1071                         byte[] buf = new byte [bufLen];
1072                         using (Stream s = vf.Open ()) {
1073                                 int readBytes;
1074                                 while ((readBytes = s.Read (buf, 0, bufLen)) > 0) {
1075                                         output_stream.Write (buf, 0, readBytes);
1076                                         output_stream.ApplyFilter (final_flush);
1077                                         Flush (false);
1078                                 }
1079                                 if (final_flush)
1080                                         Flush (true);
1081                         }
1082                 }
1083 #endif
1084                 
1085 #region Session state support
1086                 internal void SetAppPathModifier (string app_modifier)
1087                 {
1088                         app_path_mod = app_modifier;
1089                 }
1090 #endregion
1091                 
1092 #region Cache Support
1093                 internal void SetCachedHeaders (NameValueCollection headers)
1094                 {
1095                         cached_headers = headers;
1096                         
1097                 }
1098
1099                 internal bool IsCached {
1100                         get { return cached_response != null; }
1101                         set {
1102                                 if (value)
1103                                         cached_response = new CachedRawResponse (cache_policy);
1104                                 else
1105                                         cached_response = null;
1106                         }
1107                 }
1108
1109                 public HttpCachePolicy Cache {
1110                         get {
1111                                 if (cache_policy == null)
1112                                         cache_policy = new HttpCachePolicy ();
1113                                 
1114                                 return cache_policy;
1115                         }
1116                 }               
1117
1118                 internal CachedRawResponse GetCachedResponse ()
1119                 {
1120                         if (cached_response != null) {
1121                                 cached_response.StatusCode = StatusCode;
1122                                 cached_response.StatusDescription = StatusDescription;
1123                         }
1124                         
1125                         return cached_response;
1126                 }
1127
1128                 //
1129                 // This is one of the old ASP compatibility methods, the real cache
1130                 // control is in the Cache property, and this is a second class citizen
1131                 //
1132                 public string CacheControl {
1133                         set {
1134                                 if (value == null || value == "") {
1135                                         Cache.SetCacheability (HttpCacheability.NoCache);
1136                                         user_cache_control = null;
1137                                 } else if (String.Compare (value, "public", true, CultureInfo.InvariantCulture) == 0) {
1138                                         Cache.SetCacheability (HttpCacheability.Public);
1139                                         user_cache_control = "public";
1140                                 } else if (String.Compare (value, "private", true, CultureInfo.InvariantCulture) == 0) {
1141                                         Cache.SetCacheability (HttpCacheability.Private);
1142                                         user_cache_control = "private";
1143                                 } else if (String.Compare (value, "no-cache", true, CultureInfo.InvariantCulture) == 0) {
1144                                         Cache.SetCacheability (HttpCacheability.NoCache);
1145                                         user_cache_control = "no-cache";
1146                                 } else
1147                                         throw new ArgumentException ("CacheControl property only allows `public', " +
1148                                                                      "`private' or no-cache, for different uses, use " +
1149                                                                      "Response.AppendHeader");
1150                         }
1151
1152                         get { return (user_cache_control != null) ? user_cache_control : "private"; }
1153                 }
1154 #endregion
1155
1156                 internal int GetOutputByteCount ()
1157                 {
1158                         return output_stream.GetTotalLength ();
1159                 }
1160
1161                 internal void ReleaseResources ()
1162                 {
1163                         output_stream.ReleaseResources (true);
1164                         output_stream = null;
1165                 }
1166         }
1167
1168 #if TARGET_J2EE
1169         public 
1170 #endif  
1171         static class FlagEnd
1172         {
1173                 public static readonly object Value = new object ();
1174         }
1175 }
1176