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