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