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