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