09207ff7ed6571148c6015207d57dc419488076a
[mono.git] / mcs / class / System.Web / System.Web / HttpResponse.cs
1 //
2 // System.Web.HttpResponse.cs 
3 //
4 // 
5 // Author:
6 //      Miguel de Icaza (miguel@novell.com)
7 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
8 //
9 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System.Text;
32 using System.Web.UI;
33 using System.Collections;
34 using System.Collections.Specialized;
35 using System.IO;
36 using System.Web.Caching;
37 using System.Threading;
38 using System.Web.Util;
39 using System.Web.Configuration;
40 using System.Globalization;
41 using System.Security.Permissions;
42 using System.Web.Hosting;
43 using System.Web.Configuration;
44 using System.Web.SessionState;
45
46 namespace System.Web {
47         
48         // CAS - no InheritanceDemand here as the class is sealed
49         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
50         public sealed partial class HttpResponse {
51                 internal HttpWorkerRequest WorkerRequest;
52                 internal HttpResponseStream output_stream;
53                 internal bool buffer = true;
54
55                 ArrayList fileDependencies;
56                 
57                 HttpContext context;
58                 TextWriter writer;
59                 HttpCachePolicy cache_policy;
60                 Encoding encoding;
61                 HttpCookieCollection cookies;
62                 
63                 int status_code = 200;
64                 string status_description = "OK";
65
66                 string content_type = "text/html";
67                 string charset;
68                 bool charset_set;
69                 CachedRawResponse cached_response;
70                 string user_cache_control = "private";
71                 string redirect_location;
72                 
73                 //
74                 // Negative Content-Length means we auto-compute the size of content-length
75                 // can be overwritten with AppendHeader ("Content-Length", value)
76                 //
77                 long content_length = -1;
78
79                 //
80                 // The list of the headers that we will send back to the client, except
81                 // the headers that we compute here.
82                 //
83                 ArrayList headers = new ArrayList ();
84                 bool headers_sent;
85                 ArrayList cached_headers;
86
87                 //
88                 // Transfer encoding state
89                 //
90                 string transfer_encoding;
91                 internal bool use_chunked;
92                 
93                 bool closed;
94                 internal bool suppress_content;
95
96                 //
97                 // Session State
98                 //
99                 string app_path_mod;
100                 
101 #if NET_2_0
102                 bool is_request_being_redirected;
103                 Encoding headerEncoding;
104 #endif
105                 
106                 internal HttpResponse ()
107                 {
108                         output_stream = new HttpResponseStream (this);
109                 }
110
111                 public HttpResponse (TextWriter writer) : this ()
112                 {
113                         this.writer = writer;
114                 }
115
116                 internal HttpResponse (HttpWorkerRequest worker_request, HttpContext context) : this ()
117                 {
118                         WorkerRequest = worker_request;
119                         this.context = context;
120
121 #if !TARGET_J2EE
122                         if (worker_request != null)
123                                 use_chunked = (worker_request.GetHttpVersion () == "HTTP/1.1");
124 #endif
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 !TARGET_J2EE
470                         if (String.Compare (name, "content-length", true, CultureInfo.InvariantCulture) == 0){
471                                 content_length = (long) UInt64.Parse (value);
472                                 use_chunked = false;
473                                 return;
474                         }
475 #endif
476
477                         if (String.Compare (name, "content-type", true, CultureInfo.InvariantCulture) == 0){
478                                 ContentType = value;
479                                 return;
480                         }
481
482 #if !TARGET_J2EE
483                         if (String.Compare (name, "transfer-encoding", true, CultureInfo.InvariantCulture) == 0){
484                                 transfer_encoding = value;
485                                 use_chunked = false;
486                                 return;
487                         }
488 #endif
489
490                         if (String.Compare (name, "cache-control", true, CultureInfo.InvariantCulture) == 0){
491                                 user_cache_control = value;
492                                 return;
493                         }
494
495                         headers.Add (new UnknownResponseHeader (name, value));
496                 }
497
498                 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
499                 public void AppendToLog (string param)
500                 {
501                         Console.Write ("System.Web: ");
502                         Console.WriteLine (param);
503                 }
504                 
505                 public string ApplyAppPathModifier (string virtualPath)
506                 {
507                         if (virtualPath == null)
508                                 return null;
509                 
510                         if (virtualPath == "")
511                                 return context.Request.RootVirtualDir;
512
513                         if (UrlUtils.IsRelativeUrl (virtualPath)) {
514                                 virtualPath = UrlUtils.Combine (context.Request.RootVirtualDir, virtualPath);
515                         } else if (UrlUtils.IsRooted (virtualPath)) {
516                                 virtualPath = UrlUtils.Canonic (virtualPath);
517                         }
518
519                         bool cookieless = false;
520 #if NET_2_0
521                         SessionStateSection config = WebConfigurationManager.GetSection ("system.web/sessionState") as SessionStateSection;
522                         cookieless = SessionStateModule.IsCookieLess (context, config);
523 #else
524                         SessionConfig config = HttpContext.GetAppConfig ("system.web/sessionState") as SessionConfig;
525                         cookieless = config.CookieLess;
526 #endif
527                         if (!cookieless)
528                                 return virtualPath;
529
530                         if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) < 0) {
531                                 if (UrlUtils.HasSessionId (virtualPath))
532                                         virtualPath = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (virtualPath), virtualPath);
533                                 return UrlUtils.InsertSessionId (app_path_mod, virtualPath);
534                         }
535                 
536                         return virtualPath;
537                 }
538
539                 public void BinaryWrite (byte [] buffer)
540                 {
541                         output_stream.Write (buffer, 0, buffer.Length);
542                 }
543
544                 internal void BinaryWrite (byte [] buffer, int start, int len)
545                 {
546                         output_stream.Write (buffer, start, len);
547                 }
548
549                 public void Clear ()
550                 {
551                         ClearContent ();
552                 }
553
554                 public void ClearContent ()
555                 {
556                         output_stream.Clear ();
557                         content_length = -1;
558                 }
559
560                 public void ClearHeaders ()
561                 {
562                         if (headers_sent)
563                                 throw new HttpException ("headers have been already sent");
564
565                         // Reset the special case headers.
566                         content_length = -1;
567                         content_type = "text/html";
568                         transfer_encoding = null;
569                         user_cache_control = null;
570                         headers.Clear ();
571                 }
572
573                 internal bool HeadersSent {
574                         get {
575                                 return headers_sent;
576                         }
577                 }
578
579                 public void Close ()
580                 {
581                         if (closed)
582                                 return;
583                         if (WorkerRequest != null)
584                                 WorkerRequest.CloseConnection ();
585                         closed = true;
586                 }
587
588                 public void End ()
589                 {
590                         if (context == null)
591                                 return;
592                         
593                         if (context.TimeoutPossible) {
594                                 Thread.CurrentThread.Abort (FlagEnd.Value);
595                         } else {
596                                 // If this is called from an async event, signal the completion
597                                 // but don't throw.
598                                 HttpApplication app_instance = context.ApplicationInstance;
599                                 if (app_instance != null)
600                                         app_instance.CompleteRequest ();
601                         }
602                 }
603
604                 // Generate:
605                 //   Content-Length
606                 //   Content-Type
607                 //   Transfer-Encoding (chunked)
608                 //   Cache-Control
609                 void AddHeadersNoCache (ArrayList write_headers, bool final_flush)
610                 {
611 #if !TARGET_J2EE
612                         //
613                         // Transfer-Encoding
614                         //
615                         if (use_chunked)
616                                 write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", "chunked"));
617                         else if (transfer_encoding != null)
618                                 write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", transfer_encoding));
619 #endif
620                         if (redirect_location != null)
621                                 write_headers.Add (new UnknownResponseHeader ("Location", redirect_location));
622                         
623 #if !TARGET_J2EE
624                         //
625                         // If Content-Length is set.
626                         //
627                         if (content_length >= 0) {
628                                 write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderContentLength,
629                                                                       content_length.ToString (CultureInfo.InvariantCulture)));
630                         } else if (BufferOutput) {
631                                 if (final_flush) {                                      
632                                         //
633                                         // If we are buffering and this is the last flush, not a middle-flush,
634                                         // we know the content-length.
635                                         //
636                                         content_length = output_stream.total;
637                                         write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderContentLength,
638                                                                               content_length.ToString (CultureInfo.InvariantCulture)));
639                                 } else {
640                                         //
641                                         // We are buffering, and this is a flush in the middle.
642                                         // If we are not chunked, we need to set "Connection: close".
643                                         //
644                                         if (use_chunked){
645                                                 write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
646                                         }
647                                 }
648                         } else {
649                                 //
650                                 // If the content-length is not set, and we are not buffering, we must
651                                 // close at the end.
652                                 //
653                                 if (use_chunked){
654                                         write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
655                                 }
656                         }
657 #endif
658
659                         //
660                         // Cache Control, the cache policy takes precedence over the cache_control property.
661                         //
662                         if (cache_policy != null)
663                                 cache_policy.SetHeaders (this, headers);
664                         else
665                                 write_headers.Add (new UnknownResponseHeader ("Cache-Control", CacheControl));
666                         
667                         //
668                         // Content-Type
669                         //
670                         if (content_type != null){
671                                 string header = content_type;
672
673                                 if (charset_set || header == "text/plain" || header == "text/html") {
674                                         if (header.IndexOf ("charset=") == -1) {
675                                                 if (charset == null || charset == "")
676                                                         charset = ContentEncoding.HeaderName;
677                                                 header += "; charset=" + charset;
678                                         }
679                                 }
680                                 
681                                 write_headers.Add (new UnknownResponseHeader ("Content-Type", header));
682                         }
683
684                         if (cookies != null && cookies.Count != 0){
685                                 int n = cookies.Count;
686                                 for (int i = 0; i < n; i++)
687                                         write_headers.Add (cookies.Get (i).GetCookieHeader ());
688 #if TARGET_J2EE
689                                 // For J2EE Portal support emulate cookies by storing them in the session.
690                                 context.Request.SetSessionCookiesForPortal (cookies);
691 #endif
692                         }
693                 }
694
695                 internal void WriteHeaders (bool final_flush)
696                 {
697                         if (headers_sent)
698                                 return;
699
700                         //
701                         // Flush
702                         //
703                         if (context != null) {
704                                 HttpApplication app_instance = context.ApplicationInstance;
705                                 if (app_instance != null)
706                                         app_instance.TriggerPreSendRequestHeaders ();
707                         }
708
709                         headers_sent = true;
710
711                         if (cached_response != null)
712                                 cached_response.SetHeaders (headers);
713
714                         // If this page is cached use the cached headers
715                         // instead of the standard headers      
716                         ArrayList write_headers = headers;
717                         if (cached_headers != null)
718                                 write_headers = cached_headers;
719                         else
720                                 AddHeadersNoCache (write_headers, final_flush);
721
722                         if (WorkerRequest != null)
723                                 WorkerRequest.SendStatus (status_code, StatusDescription);
724
725                         if (WorkerRequest != null) {
726                                 foreach (BaseResponseHeader header in write_headers){
727                                         header.SendContent (WorkerRequest);
728                                 }
729                         }
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 #if NET_2_0
974                 internal void TransmitFile (VirtualFile vf)
975                 {
976                         TransmitFile (vf, false);
977                 }
978                 
979                 internal void TransmitFile (VirtualFile vf, bool final_flush)
980                 {
981                         if (vf == null)
982                                 throw new ArgumentNullException ("vf");
983
984                         using (Stream s = vf.Open ()) {
985                                 long len = s.Length;
986                                 byte[] buf = new byte [len];
987                                 int readBytes = s.Read (buf, 0, (int) len);
988                                 output_stream.Write (buf, 0, readBytes);
989                                 output_stream.ApplyFilter (final_flush);
990                                 Flush (final_flush);
991                         }
992                 }
993 #endif
994                 
995 #region Session state support
996                 internal void SetAppPathModifier (string app_modifier)
997                 {
998                         app_path_mod = app_modifier;
999                 }
1000 #endregion
1001                 
1002 #region Cache Support
1003                 internal void SetCachedHeaders (ArrayList headers)
1004                 {
1005                         cached_headers = headers;
1006                 }
1007
1008                 internal bool IsCached {
1009                         get {
1010                                 return cached_response != null;
1011                         }
1012                 }
1013
1014                 public HttpCachePolicy Cache {
1015                         get {
1016                                 if (cache_policy == null) {
1017                                         cache_policy = new HttpCachePolicy ();
1018                                         cache_policy.CacheabilityUpdated += new CacheabilityUpdatedCallback (OnCacheabilityUpdated);
1019                                 }
1020                                 
1021                                 return cache_policy;
1022                         }
1023                 }
1024                 
1025                 private void OnCacheabilityUpdated (object sender, CacheabilityUpdatedEventArgs e)
1026                 {
1027                         if (e.Cacheability >= HttpCacheability.Server && !IsCached)
1028                                 cached_response = new CachedRawResponse (cache_policy);
1029                         else if (e.Cacheability <= HttpCacheability.Private)
1030                                 cached_response = null;
1031                 }
1032
1033                 internal CachedRawResponse GetCachedResponse ()
1034                 {
1035                         cached_response.StatusCode = StatusCode;
1036                         cached_response.StatusDescription = StatusDescription;
1037                         return cached_response;
1038                 }
1039
1040                 //
1041                 // This is one of the old ASP compatibility methods, the real cache
1042                 // control is in the Cache property, and this is a second class citizen
1043                 //
1044                 public string CacheControl {
1045                         set {
1046                                 if (value == null || value == "") {
1047                                         Cache.SetCacheability (HttpCacheability.NoCache);
1048                                         user_cache_control = null;
1049                                 } else if (String.Compare (value, "public", true, CultureInfo.InvariantCulture) == 0) {
1050                                         Cache.SetCacheability (HttpCacheability.Public);
1051                                         user_cache_control = "public";
1052                                 } else if (String.Compare (value, "private", true, CultureInfo.InvariantCulture) == 0) {
1053                                         Cache.SetCacheability (HttpCacheability.Private);
1054                                         user_cache_control = "private";
1055                                 } else if (String.Compare (value, "no-cache", true, CultureInfo.InvariantCulture) == 0) {
1056                                         Cache.SetCacheability (HttpCacheability.NoCache);
1057                                         user_cache_control = "no-cache";
1058                                 } else
1059                                         throw new ArgumentException ("CacheControl property only allows `public', " +
1060                                                                      "`private' or no-cache, for different uses, use " +
1061                                                                      "Response.AppendHeader");
1062                         }
1063
1064                         get { return (user_cache_control != null) ? user_cache_control : "private"; }
1065                 }
1066 #endregion
1067
1068                 internal int GetOutputByteCount ()
1069                 {
1070                         return output_stream.GetTotalLength ();
1071                 }
1072
1073                 internal void ReleaseResources ()
1074                 {
1075                         output_stream.ReleaseResources (true);
1076                         output_stream = null;
1077                 }
1078         }
1079
1080 #if TARGET_J2EE
1081         public 
1082 #endif  
1083         static class FlagEnd
1084         {
1085                 public static readonly object Value = new object ();
1086         }
1087 }
1088