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