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