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