2009-02-28 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.GetWebApplicationSection ("system.web/httpRuntime") as HttpRuntimeSection;
111 #else
112                         HttpRuntimeConfig config = HttpContext.GetAppConfig ("system.web/httpRuntime") as HttpRuntimeConfig;
113 #endif
114                         if (config != null && 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.GetWebApplicationSection ("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 #if NET_2_0
603                 public void DisableKernelCache ()
604                 {
605                         // does nothing in Mono
606                 }
607 #endif
608                 
609                 public void End ()
610                 {
611                         if (context == null)
612                                 return;
613                         
614                         if (context.TimeoutPossible) {
615                                 Thread.CurrentThread.Abort (FlagEnd.Value);
616                         } else {
617                                 // If this is called from an async event, signal the completion
618                                 // but don't throw.
619                                 HttpApplication app_instance = context.ApplicationInstance;
620                                 if (app_instance != null)
621                                         app_instance.CompleteRequest ();
622                         }
623                 }
624
625                 // Generate:
626                 //   Content-Length
627                 //   Content-Type
628                 //   Transfer-Encoding (chunked)
629                 //   Cache-Control
630                 //   X-AspNet-Version
631                 void AddHeadersNoCache (ArrayList write_headers, bool final_flush)
632                 {
633 #if !TARGET_J2EE
634                         //
635                         // Transfer-Encoding
636                         //
637                         if (use_chunked)
638                                 write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", "chunked"));
639                         else if (transfer_encoding != null)
640                                 write_headers.Add (new UnknownResponseHeader ("Transfer-Encoding", transfer_encoding));
641 #endif
642                         if (redirect_location != null)
643                                 write_headers.Add (new UnknownResponseHeader ("Location", redirect_location));
644                         
645 #if !TARGET_J2EE
646                         if (version_header != null)
647                                 write_headers.Add (version_header);
648
649                         //
650                         // If Content-Length is set.
651                         //
652                         if (content_length >= 0) {
653                                 write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderContentLength,
654                                                                       content_length.ToString (CultureInfo.InvariantCulture)));
655                         } else if (BufferOutput) {
656                                 if (final_flush) {                                      
657                                         //
658                                         // If we are buffering and this is the last flush, not a middle-flush,
659                                         // we know the content-length.
660                                         //
661                                         content_length = output_stream.total;
662                                         write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderContentLength,
663                                                                               content_length.ToString (CultureInfo.InvariantCulture)));
664                                 } else {
665                                         //
666                                         // We are buffering, and this is a flush in the middle.
667                                         // If we are not chunked, we need to set "Connection: close".
668                                         //
669                                         if (use_chunked){
670                                                 write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
671                                         }
672                                 }
673                         } else {
674                                 //
675                                 // If the content-length is not set, and we are not buffering, we must
676                                 // close at the end.
677                                 //
678                                 if (use_chunked){
679                                         write_headers.Add (new KnownResponseHeader (HttpWorkerRequest.HeaderConnection, "close"));
680                                 }
681                         }
682 #endif
683
684                         //
685                         // Cache Control, the cache policy takes precedence over the cache_control property.
686                         //
687                         if (cache_policy != null)
688                                 cache_policy.SetHeaders (this, headers);
689                         else
690                                 write_headers.Add (new UnknownResponseHeader ("Cache-Control", CacheControl));
691                         
692                         //
693                         // Content-Type
694                         //
695                         if (content_type != null){
696                                 string header = content_type;
697
698                                 if (charset_set || header == "text/plain" || header == "text/html") {
699                                         if (header.IndexOf ("charset=") == -1) {
700                                                 if (charset == null || charset == "")
701                                                         charset = ContentEncoding.HeaderName;
702                                                 header += "; charset=" + charset;
703                                         }
704                                 }
705                                 
706                                 write_headers.Add (new UnknownResponseHeader ("Content-Type", header));
707                         }
708
709                         if (cookies != null && cookies.Count != 0){
710                                 int n = cookies.Count;
711                                 for (int i = 0; i < n; i++)
712                                         write_headers.Add (cookies.Get (i).GetCookieHeader ());
713 #if TARGET_J2EE
714                                 // For J2EE Portal support emulate cookies by storing them in the session.
715                                 context.Request.SetSessionCookiesForPortal (cookies);
716 #endif
717                         }
718                 }
719
720                 internal void WriteHeaders (bool final_flush)
721                 {
722                         if (headers_sent)
723                                 return;
724
725                         //
726                         // Flush
727                         //
728                         if (context != null) {
729                                 HttpApplication app_instance = context.ApplicationInstance;
730                                 if (app_instance != null)
731                                         app_instance.TriggerPreSendRequestHeaders ();
732                         }
733
734                         headers_sent = true;
735
736                         if (cached_response != null)
737                                 cached_response.SetHeaders (headers);
738
739                         // If this page is cached use the cached headers
740                         // instead of the standard headers      
741                         ArrayList write_headers = headers;
742                         if (cached_headers != null)
743                                 write_headers = cached_headers;
744                         else
745                                 AddHeadersNoCache (write_headers, final_flush);
746
747                         if (WorkerRequest != null)
748                                 WorkerRequest.SendStatus (status_code, StatusDescription);
749
750                         if (WorkerRequest != null) {
751                                 foreach (BaseResponseHeader header in write_headers){
752                                         header.SendContent (WorkerRequest);
753                                 }
754                         }
755                 }
756
757                 internal void DoFilter (bool close)
758                 {
759                         if (output_stream.HaveFilter && context != null && context.Error == null)
760                                 output_stream.ApplyFilter (close);
761                 }
762
763                 internal void Flush (bool final_flush)
764                 {
765                         DoFilter (final_flush);
766                         if (!headers_sent){
767                                 if (final_flush || status_code != 200)
768                                         use_chunked = false;
769                         }
770
771                         bool head = ((context != null) && (context.Request.HttpMethod == "HEAD"));
772                         if (suppress_content || head) {
773                                 if (!headers_sent)
774                                         WriteHeaders (true);
775                                 output_stream.Clear ();
776                                 if (WorkerRequest != null)
777                                         output_stream.Flush (WorkerRequest, true); // ignore final_flush here.
778                                 return;
779                         }
780
781                         if (!headers_sent)
782                                 WriteHeaders (final_flush);
783
784                         if (context != null) {
785                                 HttpApplication app_instance = context.ApplicationInstance;
786                                 if (app_instance != null)
787                                         app_instance.TriggerPreSendRequestContent ();
788                         }
789
790                         if (IsCached) {
791                                 MemoryStream ms = output_stream.GetData ();
792                                 cached_response.ContentLength = (int) ms.Length;
793                                 cached_response.SetData (ms.GetBuffer ());
794                         }
795
796                         if (WorkerRequest != null)
797                                 output_stream.Flush (WorkerRequest, final_flush);
798                 }
799
800                 public void Flush ()
801                 {
802                         Flush (false);
803                 }
804
805                 public void Pics (string value)
806                 {
807                         AppendHeader ("PICS-Label", value);
808                 }
809
810                 public void Redirect (string url)
811                 {
812                         Redirect (url, true);
813                 }
814
815                 public void Redirect (string url, bool endResponse)
816                 {
817                         if (headers_sent)
818                                 throw new HttpException ("Headers have already been sent");
819
820 #if NET_2_0
821                         is_request_being_redirected = true;
822 #endif
823                         ClearHeaders ();
824                         ClearContent ();
825                         
826                         StatusCode = 302;
827                         url = ApplyAppPathModifier (url);
828                         redirect_location = url;
829
830                         // Text for browsers that can't handle location header
831                         Write ("<html><head><title>Object moved</title></head><body>\r\n");
832                         Write ("<h2>Object moved to <a href=\"" + url + "\">here</a></h2>\r\n");
833                         Write ("</body><html>\r\n");
834                         
835                         if (endResponse)
836                                 End ();
837 #if NET_2_0
838                         is_request_being_redirected = false;
839 #endif
840                 }
841
842                 public static void RemoveOutputCacheItem (string path)
843                 {
844                         if (path == null)
845                                 throw new ArgumentNullException ("path");
846
847                         if (path.Length == 0)
848                                 return;
849
850                         if (path [0] != '/')
851                                 throw new ArgumentException ("'" + path + "' is not an absolute virtual path.");
852
853                         HttpRuntime.InternalCache.Remove (path);
854                 }
855
856                 public void SetCookie (HttpCookie cookie)
857                 {
858                         AppendCookie (cookie);
859                 }
860
861                 public void Write (char ch)
862                 {
863                         Output.Write (ch);
864                 }
865
866                 public void Write (object obj)
867                 {
868                         if (obj == null)
869                                 return;
870                         
871                         Output.Write (obj.ToString ());
872                 }
873                 
874                 public void Write (string s)
875                 {
876                         Output.Write (s);
877                 }
878                 
879                 public void Write (char [] buffer, int index, int count)
880                 {
881                         Output.Write (buffer, index, count);
882                 }
883
884                 internal void WriteFile (FileStream fs, long offset, long size)
885                 {
886                         byte [] buffer = new byte [32*1024];
887
888                         if (offset != 0)
889                                 fs.Position = offset;
890
891                         long remain = size;
892                         int n;
893                         while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
894                                 remain -= n;
895                                 output_stream.Write (buffer, 0, n);
896                         }
897                 }
898                 
899                 public void WriteFile (string filename)
900                 {
901                         WriteFile (filename, false);
902                 }
903
904                 public void WriteFile (string filename, bool readIntoMemory)
905                 {
906                         if (filename == null)
907                                 throw new ArgumentNullException ("filename");
908
909                         if (readIntoMemory){
910                                 using (FileStream fs = File.OpenRead (filename))
911                                         WriteFile (fs, 0, fs.Length);
912                         } else {
913                                 FileInfo fi = new FileInfo (filename);
914                                 output_stream.WriteFile (filename, 0, fi.Length);
915                         }
916                         if (buffer)
917                                 return;
918
919                         output_stream.ApplyFilter (false);
920                         Flush ();
921                 }
922
923 #if TARGET_JVM
924                 public void WriteFile (IntPtr fileHandle, long offset, long size) {
925                         throw new PlatformNotSupportedException("IntPtr not supported");
926                 }
927 #else
928                 public void WriteFile (IntPtr fileHandle, long offset, long size)
929                 {
930                         if (offset < 0)
931                                 throw new ArgumentNullException ("offset can not be negative");
932                         if (size < 0)
933                                 throw new ArgumentNullException ("size can not be negative");
934
935                         if (size == 0)
936                                 return;
937
938                         // Note: this .ctor will throw a SecurityException if the caller 
939                         // doesn't have the UnmanagedCode permission
940                         using (FileStream fs = new FileStream (fileHandle, FileAccess.Read))
941                                 WriteFile (fs, offset, size);
942
943                         if (buffer)
944                                 return;
945                         output_stream.ApplyFilter (false);
946                         Flush ();
947                 }
948 #endif
949
950                 public void WriteFile (string filename, long offset, long size)
951                 {
952                         if (filename == null)
953                                 throw new ArgumentNullException ("filename");
954                         if (offset < 0)
955                                 throw new ArgumentNullException ("offset can not be negative");
956                         if (size < 0)
957                                 throw new ArgumentNullException ("size can not be negative");
958
959                         if (size == 0)
960                                 return;
961                         
962                         FileStream fs = File.OpenRead (filename);
963                         WriteFile (fs, offset, size);
964
965                         if (buffer)
966                                 return;
967
968                         output_stream.ApplyFilter (false);
969                         Flush ();
970                 }
971 #if NET_2_0
972                 [MonoTODO ("Not implemented")]
973                 public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
974                 {
975                         throw new NotImplementedException ();
976                 }
977 #endif
978                 //
979                 // Like WriteFile, but never buffers, so we manually Flush here
980                 //
981                 public void TransmitFile (string filename) 
982                 {
983                         if (filename == null)
984                                 throw new ArgumentNullException ("filename");
985
986                         TransmitFile (filename, false);
987                 }
988
989                 internal void TransmitFile (string filename, bool final_flush)
990                 {
991                         FileInfo fi = new FileInfo (filename);
992                         using (Stream s = fi.OpenRead ()); // Just check if we can read.
993                         output_stream.WriteFile (filename, 0, fi.Length);
994                         output_stream.ApplyFilter (final_flush);
995                         Flush (final_flush);
996                 }
997
998 #if NET_2_0
999                 public void TransmitFile (string filename, long offset, long length)
1000                 {
1001                         output_stream.WriteFile (filename, offset, length);
1002                         output_stream.ApplyFilter (false);
1003                         Flush (false);
1004                 }
1005                 
1006                 internal void TransmitFile (VirtualFile vf)
1007                 {
1008                         TransmitFile (vf, false);
1009                 }
1010
1011                 const int bufLen = 65535;
1012                 internal void TransmitFile (VirtualFile vf, bool final_flush)
1013                 {
1014                         if (vf == null)
1015                                 throw new ArgumentNullException ("vf");
1016
1017                         if (vf is DefaultVirtualFile) {
1018                                 TransmitFile (HostingEnvironment.MapPath (vf.VirtualPath), final_flush);
1019                                 return;
1020                         }
1021                         
1022                         byte[] buf = new byte [bufLen];
1023                         using (Stream s = vf.Open ()) {
1024                                 int readBytes;
1025                                 while ((readBytes = s.Read (buf, 0, bufLen)) > 0) {
1026                                         output_stream.Write (buf, 0, readBytes);
1027                                         output_stream.ApplyFilter (final_flush);
1028                                         Flush (false);
1029                                 }
1030                                 if (final_flush)
1031                                         Flush (true);
1032                         }
1033                 }
1034 #endif
1035                 
1036 #region Session state support
1037                 internal void SetAppPathModifier (string app_modifier)
1038                 {
1039                         app_path_mod = app_modifier;
1040                 }
1041 #endregion
1042                 
1043 #region Cache Support
1044                 internal void SetCachedHeaders (ArrayList headers)
1045                 {
1046                         cached_headers = headers;
1047                 }
1048
1049                 internal bool IsCached {
1050                         get { return cached_response != null; }
1051                         set {
1052                                 if (value)
1053                                         cached_response = new CachedRawResponse (cache_policy);
1054                                 else
1055                                         cached_response = null;
1056                         }
1057                 }
1058
1059                 public HttpCachePolicy Cache {
1060                         get {
1061                                 if (cache_policy == null)
1062                                         cache_policy = new HttpCachePolicy ();
1063                                 
1064                                 return cache_policy;
1065                         }
1066                 }               
1067
1068                 internal CachedRawResponse GetCachedResponse ()
1069                 {
1070                         if (cached_response != null) {
1071                                 cached_response.StatusCode = StatusCode;
1072                                 cached_response.StatusDescription = StatusDescription;
1073                         }
1074                         
1075                         return cached_response;
1076                 }
1077
1078                 //
1079                 // This is one of the old ASP compatibility methods, the real cache
1080                 // control is in the Cache property, and this is a second class citizen
1081                 //
1082                 public string CacheControl {
1083                         set {
1084                                 if (value == null || value == "") {
1085                                         Cache.SetCacheability (HttpCacheability.NoCache);
1086                                         user_cache_control = null;
1087                                 } else if (String.Compare (value, "public", true, CultureInfo.InvariantCulture) == 0) {
1088                                         Cache.SetCacheability (HttpCacheability.Public);
1089                                         user_cache_control = "public";
1090                                 } else if (String.Compare (value, "private", true, CultureInfo.InvariantCulture) == 0) {
1091                                         Cache.SetCacheability (HttpCacheability.Private);
1092                                         user_cache_control = "private";
1093                                 } else if (String.Compare (value, "no-cache", true, CultureInfo.InvariantCulture) == 0) {
1094                                         Cache.SetCacheability (HttpCacheability.NoCache);
1095                                         user_cache_control = "no-cache";
1096                                 } else
1097                                         throw new ArgumentException ("CacheControl property only allows `public', " +
1098                                                                      "`private' or no-cache, for different uses, use " +
1099                                                                      "Response.AppendHeader");
1100                         }
1101
1102                         get { return (user_cache_control != null) ? user_cache_control : "private"; }
1103                 }
1104 #endregion
1105
1106                 internal int GetOutputByteCount ()
1107                 {
1108                         return output_stream.GetTotalLength ();
1109                 }
1110
1111                 internal void ReleaseResources ()
1112                 {
1113                         output_stream.ReleaseResources (true);
1114                         output_stream = null;
1115                 }
1116         }
1117
1118 #if TARGET_J2EE
1119         public 
1120 #endif  
1121         static class FlagEnd
1122         {
1123                 public static readonly object Value = new object ();
1124         }
1125 }
1126