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