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