[sre] Do we ever try to instantiate a generic instance? Assert that we don't.
[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                 public bool SuppressFormsAuthenticationRedirect {
398                         get;
399                         set;
400                 }
401
402                 public bool TrySkipIisCustomErrors {
403                         get;
404                         set;
405                 }
406                 
407                 public int StatusCode {
408                         get {
409                                 return status_code;
410                         }
411
412                         set {
413                                 if (headers_sent)
414                                         throw new HttpException ("headers have already been sent");
415                                 
416                                 status_code = value;
417                                 status_description = null;
418                         }
419                 }
420
421                 public string StatusDescription {
422                         get {
423                                 if (status_description == null)
424                                         status_description = HttpWorkerRequest.GetStatusDescription (status_code);
425
426                                 return status_description;
427                         }
428
429                         set {
430                                 if (headers_sent)
431                                         throw new HttpException ("headers have already been sent");
432                                 
433                                 status_description = value;
434                         }
435                 }
436                 
437                 public bool SuppressContent {
438                         get {
439                                 return suppress_content;
440                         }
441
442                         set {
443                                 suppress_content = value;
444                         }
445                 }
446
447                 [MonoTODO ("Not implemented")]
448                 public void AddCacheDependency (params CacheDependency[] dependencies)
449                 {
450                         throw new NotImplementedException ();
451                 }
452
453                 [MonoTODO ("Not implemented")]
454                 public void AddCacheItemDependencies (string[] cacheKeys)
455                 {
456                         throw new NotImplementedException ();
457                 }
458
459                 [MonoTODO("Currently does nothing")]
460                 public void AddCacheItemDependencies (ArrayList cacheKeys)
461                 {
462                         // TODO: talk to jackson about the cache
463                 }
464
465                 [MonoTODO("Currently does nothing")]
466                 public void AddCacheItemDependency (string cacheKey)
467                 {
468                         // TODO: talk to jackson about the cache
469                 }
470                 
471                 public void AddFileDependencies (ArrayList filenames)
472                 {
473                         if (filenames == null || filenames.Count == 0)
474                                 return;
475                         FileDependenciesArray.AddRange (filenames);
476                 }
477
478                 public void AddFileDependencies (string[] filenames)
479                 {
480                         if (filenames == null || filenames.Length == 0)
481                                 return;
482                         FileDependenciesArray.AddRange (filenames);
483                 }
484
485                 public void AddFileDependency (string filename)
486                 {
487                         if (filename == null || filename == String.Empty)
488                                 return;
489                         FileDependenciesArray.Add (filename);
490                 }
491
492                 public void AddHeader (string name, string value)
493                 {
494                         AppendHeader (name, value);
495                 }
496
497                 public void AppendCookie (HttpCookie cookie)
498                 {
499                         Cookies.Add (cookie);
500                 }
501
502                 //
503                 // AppendHeader:
504                 //    Special case for Content-Length, Content-Type, Transfer-Encoding and Cache-Control
505                 //
506                 //
507                 public void AppendHeader (string name, string value)
508                 {
509                         if (headers_sent)
510                                 throw new HttpException ("Headers have been already sent");
511                         if (String.Compare (name, "content-length", StringComparison.OrdinalIgnoreCase) == 0){
512                                 content_length = (long) UInt64.Parse (value);
513                                 use_chunked = false;
514                                 return;
515                         }
516
517                         if (String.Compare (name, "content-type", StringComparison.OrdinalIgnoreCase) == 0){
518                                 ContentType = value;
519                                 return;
520                         }
521
522                         if (String.Compare (name, "transfer-encoding", StringComparison.OrdinalIgnoreCase) == 0){
523                                 transfer_encoding = value;
524                                 use_chunked = false;
525                                 return;
526                         }
527
528                         if (String.Compare (name, "cache-control", StringComparison.OrdinalIgnoreCase) == 0){
529                                 user_cache_control = value;
530                                 return;
531                         }
532
533                         Headers.Add (name, value);
534                 }
535
536                 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
537                 public void AppendToLog (string param)
538                 {
539                         Console.Write ("System.Web: ");
540                         Console.WriteLine (param);
541                 }
542                 
543                 public string ApplyAppPathModifier (string virtualPath)
544                 {
545                         if (virtualPath == null || context == null)
546                                 return null;
547                 
548                         if (virtualPath.Length == 0)
549                                 return context.Request.RootVirtualDir;
550
551                         if (UrlUtils.IsRelativeUrl (virtualPath)) {
552                                 virtualPath = UrlUtils.Combine (context.Request.RootVirtualDir, virtualPath);
553                         } else if (UrlUtils.IsRooted (virtualPath)) {
554                                 virtualPath = UrlUtils.Canonic (virtualPath);
555                         }
556
557                         bool cookieless = false;
558                         SessionStateSection config = WebConfigurationManager.GetWebApplicationSection ("system.web/sessionState") as SessionStateSection;
559                         cookieless = SessionStateModule.IsCookieLess (context, config);
560
561                         if (!cookieless)
562                                 return virtualPath;
563
564                         if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) < 0) {
565                                 if (UrlUtils.HasSessionId (virtualPath))
566                                         virtualPath = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (virtualPath), virtualPath);
567                                 return UrlUtils.InsertSessionId (app_path_mod, virtualPath);
568                         }
569                 
570                         return virtualPath;
571                 }
572
573                 public void BinaryWrite (byte [] buffer)
574                 {
575                         output_stream.Write (buffer, 0, buffer.Length);
576                 }
577
578                 internal void BinaryWrite (byte [] buffer, int start, int len)
579                 {
580                         output_stream.Write (buffer, start, len);
581                 }
582
583                 public void Clear ()
584                 {
585                         ClearContent ();
586                 }
587
588                 public void ClearContent ()
589                 {
590                         output_stream.Clear ();
591                         content_length = -1;
592                 }
593
594                 public void ClearHeaders ()
595                 {
596                         if (headers_sent)
597                                 throw new HttpException ("headers have been already sent");
598
599                         // Reset the special case headers.
600                         content_length = -1;
601                         content_type = "text/html";
602                         transfer_encoding = null;
603                         user_cache_control = "private";
604                         if (cache_policy != null)
605                                 cache_policy.Cacheability = HttpCacheability.Private;
606
607                         if (headers != null)
608                                 headers.Clear ();
609                 }
610
611                 internal bool HeadersSent {
612                         get {
613                                 return headers_sent;
614                         }
615                 }
616
617                 public void Close ()
618                 {
619                         if (closed)
620                                 return;
621                         if (WorkerRequest != null)
622                                 WorkerRequest.CloseConnection ();
623                         closed = true;
624                 }
625
626                 public void DisableKernelCache ()
627                 {
628                         // does nothing in Mono
629                 }
630                 
631                 public void End ()
632                 {
633                         if (context == null)
634                                 return;
635                         
636                         if (context.TimeoutPossible) {
637                                 Thread.CurrentThread.Abort (FlagEnd.Value);
638                         } else {
639                                 // If this is called from an async event, signal the completion
640                                 // but don't throw.
641                                 HttpApplication app_instance = context.ApplicationInstance;
642                                 if (app_instance != null)
643                                         app_instance.CompleteRequest ();
644                         }
645                 }
646
647                 // Generate:
648                 //   Content-Length
649                 //   Content-Type
650                 //   Transfer-Encoding (chunked)
651                 //   Cache-Control
652                 //   X-AspNet-Version
653                 void AddHeadersNoCache (NameValueCollection write_headers, bool final_flush)
654                 {
655                         //
656                         // Transfer-Encoding
657                         //
658                         if (use_chunked)
659                                 write_headers.Add ("Transfer-Encoding", "chunked");
660                         else if (transfer_encoding != null)
661                                 write_headers.Add ("Transfer-Encoding", transfer_encoding);
662                         if (redirect_location != null)
663                                 write_headers.Add ("Location", redirect_location);
664                         
665                         string vh = VersionHeader;
666                         if (vh != null)
667                                 write_headers.Add ("X-AspNet-Version", vh);
668
669                         //
670                         // If Content-Length is set.
671                         //
672                         if (content_length >= 0) {
673                                 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
674                                                    content_length.ToString (Helpers.InvariantCulture));
675                         } else if (BufferOutput) {
676                                 if (final_flush) {                                      
677                                         //
678                                         // If we are buffering and this is the last flush, not a middle-flush,
679                                         // we know the content-length.
680                                         //
681                                         content_length = output_stream.total;
682                                         write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderContentLength),
683                                                            content_length.ToString (Helpers.InvariantCulture));
684                                 } else {
685                                         //
686                                         // We are buffering, and this is a flush in the middle.
687                                         // If we are not chunked, we need to set "Connection: close".
688                                         //
689                                         if (use_chunked){
690                                                 write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
691                                         }
692                                 }
693                         } else {
694                                 //
695                                 // If the content-length is not set, and we are not buffering, we must
696                                 // close at the end.
697                                 //
698                                 if (use_chunked){
699                                         write_headers.Add (HttpWorkerRequest.GetKnownResponseHeaderName (HttpWorkerRequest.HeaderConnection), "close");
700                                 }
701                         }
702
703                         //
704                         // Cache Control, the cache policy takes precedence over the cache_control property.
705                         //
706                         if (cache_policy != null)
707                                 cache_policy.SetHeaders (this, headers);
708                         else
709                                 write_headers.Add ("Cache-Control", CacheControl);
710                         
711                         //
712                         // Content-Type
713                         //
714                         if (content_type != null){
715                                 string header = content_type;
716
717                                 if (charset_set || header == "text/plain" || header == "text/html") {
718                                         if (header.IndexOf ("charset=") == -1 && !string.IsNullOrEmpty (charset)) {
719                                                 header += "; charset=" + charset;
720                                         }
721                                 }
722                                 
723                                 write_headers.Add ("Content-Type", header);
724                         }
725
726                         if (cookies != null && cookies.Count != 0){
727                                 int n = cookies.Count;
728                                 for (int i = 0; i < n; i++)
729                                         write_headers.Add ("Set-Cookie", cookies.Get (i).GetCookieHeaderValue ());
730                         }
731                 }
732
733                 internal void WriteHeaders (bool final_flush)
734                 {
735                         if (headers_sent)
736                                 return;
737
738                         //
739                         // Flush
740                         //
741                         if (context != null) {
742                                 HttpApplication app_instance = context.ApplicationInstance;
743                                 if (app_instance != null)
744                                         app_instance.TriggerPreSendRequestHeaders ();
745                         }
746
747                         headers_sent = true;
748
749                         if (cached_response != null)
750                                 cached_response.SetHeaders (headers);
751
752                         // If this page is cached use the cached headers
753                         // instead of the standard headers      
754                         NameValueCollection write_headers;
755                         if (cached_headers != null)
756                                 write_headers = cached_headers;
757                         else {
758                                 write_headers = Headers;
759                                 AddHeadersNoCache (write_headers, final_flush);
760                         }
761                         
762                         if (WorkerRequest != null)
763                                 WorkerRequest.SendStatus (status_code, StatusDescription);
764
765                         if (WorkerRequest != null) {
766                                 string header_name;
767                                 string[] values;
768                                 int header_index;
769                                 
770                                 for (int i = 0; i < write_headers.Count; i++) {
771                                         header_name = write_headers.GetKey (i);
772                                         header_index = HttpWorkerRequest.GetKnownResponseHeaderIndex (header_name);
773                                         values = write_headers.GetValues (i);
774                                         if (values == null)
775                                                 continue;
776                                         
777                                         foreach (string val in values) {
778                                                 if (header_index > -1)
779                                                         WorkerRequest.SendKnownResponseHeader (header_index, val);
780                                                 else
781                                                         WorkerRequest.SendUnknownResponseHeader (header_name, val);
782                                         }
783                                 }
784                         }
785                 }
786
787                 internal void DoFilter (bool close)
788                 {
789                         if (output_stream.HaveFilter && context != null && context.Error == null)
790                                 output_stream.ApplyFilter (close);
791                 }
792
793                 internal void Flush (bool final_flush)
794                 {
795                         if (completed)
796                                 throw new HttpException ("Server cannot flush a completed response");
797                         
798                         DoFilter (final_flush);
799                         if (!headers_sent){
800                                 if (final_flush || status_code != 200)
801                                         use_chunked = false;
802                         }
803
804                         bool head = ((context != null) && (context.Request.HttpMethod == "HEAD"));
805                         if (suppress_content || head) {
806                                 if (!headers_sent)
807                                         WriteHeaders (true);
808                                 output_stream.Clear ();
809                                 if (WorkerRequest != null)
810                                         output_stream.Flush (WorkerRequest, true); // ignore final_flush here.
811                                 completed = true;
812                                 return;
813                         }
814                         completed = final_flush;
815                         
816                         if (!headers_sent)
817                                 WriteHeaders (final_flush);
818
819                         if (context != null) {
820                                 HttpApplication app_instance = context.ApplicationInstance;
821                                 if (app_instance != null)
822                                         app_instance.TriggerPreSendRequestContent ();
823                         }
824
825                         if (IsCached)
826                                 cached_response.SetData (output_stream.GetData ());
827
828                         if (WorkerRequest != null)
829                                 output_stream.Flush (WorkerRequest, final_flush);
830                 }
831
832                 public void Flush ()
833                 {
834                         Flush (false);
835                 }
836
837                 public void Pics (string value)
838                 {
839                         AppendHeader ("PICS-Label", value);
840                 }
841
842                 void Redirect (string url, bool endResponse, int code)
843                 {
844                         if (url == null)
845                                 throw new ArgumentNullException ("url");
846                         
847                         if (headers_sent)
848                                 throw new HttpException ("Headers have already been sent");
849
850                         if (url.IndexOf ('\n') != -1)
851                                 throw new ArgumentException ("Redirect URI cannot contain newline characters.", "url");
852                         
853                         is_request_being_redirected = true;
854                         ClearHeaders ();
855                         ClearContent ();
856                         
857                         StatusCode = code;
858                         url = ApplyAppPathModifier (url);
859
860                         bool isFullyQualified;
861                         if (StrUtils.StartsWith (url, "http:", true) ||
862                             StrUtils.StartsWith (url, "https:", true) ||
863                             StrUtils.StartsWith (url, "file:", true) ||
864                             StrUtils.StartsWith (url, "ftp:", true))
865                                 isFullyQualified = true;
866                         else
867                                 isFullyQualified = false;
868
869                         if (!isFullyQualified) {
870                                 HttpRuntimeSection config = HttpRuntime.Section;
871                                 if (config != null && config.UseFullyQualifiedRedirectUrl) {
872                                         var ub = new UriBuilder (context.Request.Url);
873                                         int qpos = url.IndexOf ('?');
874                                         if (qpos == -1) {
875                                                 ub.Path = url;
876                                                 ub.Query = null;
877                                         } else {
878                                                 ub.Path = url.Substring (0, qpos);
879                                                 ub.Query = url.Substring (qpos + 1);
880                                         }
881                                         ub.Fragment = null;
882                                         ub.Password = null;
883                                         ub.UserName = null;
884                                         url = ub.Uri.ToString ();
885                                 }
886                         }
887                         
888                         redirect_location = url;
889
890                         // Text for browsers that can't handle location header
891                         Write ("<html><head><title>Object moved</title></head><body>\r\n");
892                         Write ("<h2>Object moved to <a href=\"" + url + "\">here</a></h2>\r\n");
893                         Write ("</body><html>\r\n");
894                         
895                         if (endResponse)
896                                 End ();
897                         is_request_being_redirected = false;
898                 }
899                 
900                 public void Redirect (string url)
901                 {
902                         Redirect (url, true);
903                 }
904
905                 public void Redirect (string url, bool endResponse)
906                 {
907                         Redirect (url, endResponse, 302);
908                 }
909                 public void RedirectPermanent (string url)
910                 {
911                         RedirectPermanent (url, true);
912                 }
913
914                 public void RedirectPermanent (string url, bool endResponse)
915                 {
916                         Redirect (url, endResponse, 301);
917                 }
918
919                 public void RedirectToRoute (object routeValues)
920                 {
921                         RedirectToRoute ("RedirectToRoute", null, new RouteValueDictionary (routeValues), 302, true);
922                 }
923
924                 public void RedirectToRoute (RouteValueDictionary routeValues)
925                 {
926                         RedirectToRoute ("RedirectToRoute", null, routeValues, 302, true);
927                 }
928
929                 public void RedirectToRoute (string routeName)
930                 {
931                         RedirectToRoute ("RedirectToRoute", routeName, null, 302, true);
932                 }
933
934                 public void RedirectToRoute (string routeName, object routeValues)
935                 {
936                         RedirectToRoute ("RedirectToRoute", routeName, new RouteValueDictionary (routeValues), 302, true);
937                 }
938
939                 public void RedirectToRoute (string routeName, RouteValueDictionary routeValues)
940                 {
941                         RedirectToRoute ("RedirectToRoute", routeName, routeValues, 302, true);
942                 }
943
944                 public void RedirectToRoutePermanent (object routeValues)
945                 {
946                         RedirectToRoute ("RedirectToRoutePermanent", null, new RouteValueDictionary (routeValues), 301, false);
947                 }
948
949                 public void RedirectToRoutePermanent (RouteValueDictionary routeValues)
950                 {
951                         RedirectToRoute ("RedirectToRoutePermanent", null, routeValues, 301, false);
952                 }
953
954                 public void RedirectToRoutePermanent (string routeName)
955                 {
956                         RedirectToRoute ("RedirectToRoutePermanent", routeName, null, 301, false);
957                 }
958
959                 public void RedirectToRoutePermanent (string routeName, object routeValues)
960                 {
961                         RedirectToRoute ("RedirectToRoutePermanent", routeName, new RouteValueDictionary (routeValues), 301, false);
962                 }               
963
964                 public void RedirectToRoutePermanent (string routeName, RouteValueDictionary routeValues)
965                 {
966                         RedirectToRoute ("RedirectToRoutePermanent", routeName, routeValues, 301, false);
967                 }
968                 
969                 void RedirectToRoute (string callerName, string routeName, RouteValueDictionary routeValues, int redirectCode, bool endResponse)
970                 {
971                         HttpContext ctx = context ?? HttpContext.Current;
972                         HttpRequest req = ctx != null ? ctx.Request : null;
973                         
974                         if (req == null)
975                                 // Let's emulate .NET
976                                 throw new NullReferenceException ();
977                         
978                         VirtualPathData vpd = RouteTable.Routes.GetVirtualPath (req.RequestContext, routeName, routeValues);
979                         string redirectUrl = vpd != null ? vpd.VirtualPath : null;
980                         if (String.IsNullOrEmpty (redirectUrl))
981                                 throw new InvalidOperationException ("No matching route found for RedirectToRoute");
982
983                         Redirect (redirectUrl, true, redirectCode);
984                 }
985
986                 public static void RemoveOutputCacheItem (string path, string providerName)
987                 {
988                         if (path == null)
989                                 throw new ArgumentNullException ("path");
990
991                         if (path.Length > 0 && path [0] != '/')
992                                 throw new ArgumentException ("Invalid path for HttpResponse.RemoveOutputCacheItem: '" + path + "'. An absolute virtual path is expected");
993
994                         OutputCache.RemoveFromProvider (path, providerName);
995                 }
996                 public static void RemoveOutputCacheItem (string path)
997                 {
998                         if (path == null)
999                                 throw new ArgumentNullException ("path");
1000
1001                         if (path.Length == 0)
1002                                 return;
1003
1004                         if (path [0] != '/')
1005                                 throw new ArgumentException ("'" + path + "' is not an absolute virtual path.");
1006
1007                         RemoveOutputCacheItem (path, OutputCache.DefaultProviderName);
1008                 }
1009
1010                 public void SetCookie (HttpCookie cookie)
1011                 {
1012                         AppendCookie (cookie);
1013                 }
1014
1015                 public void Write (char ch)
1016                 {
1017                         TextWriter writer = Output;
1018                         // Emulating .NET
1019                         if (writer == null)
1020                                 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1021                         writer.Write (ch);
1022                 }
1023
1024                 public void Write (object obj)
1025                 {
1026                         TextWriter writer = Output;
1027                         // Emulating .NET
1028                         if (writer == null)
1029                                 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1030                         if (obj == null)
1031                                 return;
1032                         
1033                         writer.Write (obj.ToString ());
1034                 }
1035                 
1036                 public void Write (string s)
1037                 {
1038                         TextWriter writer = Output;
1039                         // Emulating .NET
1040                         if (writer == null)
1041                                 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1042                         writer.Write (s);
1043                 }
1044                 
1045                 public void Write (char [] buffer, int index, int count)
1046                 {
1047                         TextWriter writer = Output;
1048                         // Emulating .NET
1049                         if (writer == null)
1050                                 throw new NullReferenceException (".NET 4.0 emulation. A null value was found where an object was required.");
1051                         writer.Write (buffer, index, count);
1052                 }
1053
1054                 bool IsFileSystemDirSeparator (char ch)
1055                 {
1056                         return ch == '\\' || ch == '/';
1057                 }
1058                 
1059                 string GetNormalizedFileName (string fn)
1060                 {
1061                         if (String.IsNullOrEmpty (fn))
1062                                 return fn;
1063
1064                         // On Linux we don't change \ to / since filenames with \ are valid. We also
1065                         // don't remove drive: designator for the same reason.
1066                         int len = fn.Length;
1067                         if (len >= 3 && fn [1] == ':' && IsFileSystemDirSeparator (fn [2]))
1068                                 return Path.GetFullPath (fn); // drive-qualified absolute file path
1069
1070                         bool startsWithDirSeparator = IsFileSystemDirSeparator (fn [0]);
1071                         if (len >= 2 && startsWithDirSeparator && IsFileSystemDirSeparator (fn [1]))
1072                                 return Path.GetFullPath (fn); // UNC path
1073
1074                         if (!startsWithDirSeparator) {
1075                                 HttpContext ctx = context ?? HttpContext.Current;
1076                                 HttpRequest req = ctx != null ? ctx.Request : null;
1077                                 
1078                                 if (req != null)
1079                                         return req.MapPath (fn);
1080                         }
1081                         
1082                         return fn; // Or should we rather throw?
1083                 }
1084                 
1085                 internal void WriteFile (FileStream fs, long offset, long size)
1086                 {
1087                         byte [] buffer = new byte [32*1024];
1088
1089                         if (offset != 0)
1090                                 fs.Position = offset;
1091
1092                         long remain = size;
1093                         int n;
1094                         while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
1095                                 remain -= n;
1096                                 output_stream.Write (buffer, 0, n);
1097                         }
1098                 }
1099                 
1100                 public void WriteFile (string filename)
1101                 {
1102                         WriteFile (filename, false);
1103                 }
1104
1105                 public void WriteFile (string filename, bool readIntoMemory)
1106                 {
1107                         if (filename == null)
1108                                 throw new ArgumentNullException ("filename");
1109
1110                         string fn = GetNormalizedFileName (filename);
1111                         if (readIntoMemory){
1112                                 using (FileStream fs = File.OpenRead (fn))
1113                                         WriteFile (fs, 0, fs.Length);
1114                         } else {
1115                                 FileInfo fi = new FileInfo (fn);
1116                                 output_stream.WriteFile (fn, 0, fi.Length);
1117                         }
1118                         if (buffer)
1119                                 return;
1120
1121                         output_stream.ApplyFilter (false);
1122                         Flush ();
1123                 }
1124
1125                 public void WriteFile (IntPtr fileHandle, long offset, long size)
1126                 {
1127                         if (offset < 0)
1128                                 throw new ArgumentNullException ("offset can not be negative");
1129                         if (size < 0)
1130                                 throw new ArgumentNullException ("size can not be negative");
1131
1132                         if (size == 0)
1133                                 return;
1134
1135                         // Note: this .ctor will throw a SecurityException if the caller 
1136                         // doesn't have the UnmanagedCode permission
1137                         using (FileStream fs = new FileStream (fileHandle, FileAccess.Read))
1138                                 WriteFile (fs, offset, size);
1139
1140                         if (buffer)
1141                                 return;
1142                         output_stream.ApplyFilter (false);
1143                         Flush ();
1144                 }
1145
1146                 public void WriteFile (string filename, long offset, long size)
1147                 {
1148                         if (filename == null)
1149                                 throw new ArgumentNullException ("filename");
1150                         if (offset < 0)
1151                                 throw new ArgumentNullException ("offset can not be negative");
1152                         if (size < 0)
1153                                 throw new ArgumentNullException ("size can not be negative");
1154
1155                         if (size == 0)
1156                                 return;
1157                         
1158                         FileStream fs = File.OpenRead (filename);
1159                         WriteFile (fs, offset, size);
1160
1161                         if (buffer)
1162                                 return;
1163
1164                         output_stream.ApplyFilter (false);
1165                         Flush ();
1166                 }
1167
1168                 public void WriteSubstitution (HttpResponseSubstitutionCallback callback)
1169                 {
1170                         // Emulation of .NET behavior
1171                         if (callback == null)
1172                                 throw new NullReferenceException ();
1173
1174                         object target = callback.Target;
1175                         if (target != null && target.GetType () == typeof (Control))
1176                                 throw new ArgumentException ("callback");
1177
1178                         string s = callback (context);
1179                         if (!IsCached) {
1180                                 Write (s);
1181                                 return;
1182                         }
1183
1184                         Cache.Cacheability = HttpCacheability.Server;
1185                         Flush ();
1186                         if (WorkerRequest == null)
1187                                 Write (s); // better this than nothing
1188                         else {
1189                                 byte[] bytes = WebEncoding.ResponseEncoding.GetBytes (s);
1190                                 WorkerRequest.SendResponseFromMemory (bytes, bytes.Length);
1191                         }
1192                         
1193                         cached_response.SetData (callback);
1194                 }
1195
1196                 //
1197                 // Like WriteFile, but never buffers, so we manually Flush here
1198                 //
1199                 public void TransmitFile (string filename) 
1200                 {
1201                         if (filename == null)
1202                                 throw new ArgumentNullException ("filename");
1203
1204                         TransmitFile (filename, false);
1205                 }
1206
1207                 internal void TransmitFile (string filename, bool final_flush)
1208                 {
1209                         FileInfo fi = new FileInfo (filename);
1210                         using (Stream s = fi.OpenRead ()); // Just check if we can read.
1211                         output_stream.WriteFile (filename, 0, fi.Length);
1212                         output_stream.ApplyFilter (final_flush);
1213                         Flush (final_flush);
1214                 }
1215
1216                 public void TransmitFile (string filename, long offset, long length)
1217                 {
1218                         output_stream.WriteFile (filename, offset, length);
1219                         output_stream.ApplyFilter (false);
1220                         Flush (false);
1221                 }
1222                 
1223                 internal void TransmitFile (VirtualFile vf)
1224                 {
1225                         TransmitFile (vf, false);
1226                 }
1227
1228                 const int bufLen = 65535;
1229                 internal void TransmitFile (VirtualFile vf, bool final_flush)
1230                 {
1231                         if (vf == null)
1232                                 throw new ArgumentNullException ("vf");
1233
1234                         if (vf is DefaultVirtualFile) {
1235                                 TransmitFile (HostingEnvironment.MapPath (vf.VirtualPath), final_flush);
1236                                 return;
1237                         }
1238                         
1239                         byte[] buf = new byte [bufLen];
1240                         using (Stream s = vf.Open ()) {
1241                                 int readBytes;
1242                                 while ((readBytes = s.Read (buf, 0, bufLen)) > 0) {
1243                                         output_stream.Write (buf, 0, readBytes);
1244                                         output_stream.ApplyFilter (final_flush);
1245                                         Flush (false);
1246                                 }
1247                                 if (final_flush)
1248                                         Flush (true);
1249                         }
1250                 }
1251                 
1252 #region Session state support
1253                 internal void SetAppPathModifier (string app_modifier)
1254                 {
1255                         app_path_mod = app_modifier;
1256                 }
1257 #endregion
1258                 
1259 #region Cache Support
1260                 internal void SetCachedHeaders (NameValueCollection headers)
1261                 {
1262                         cached_headers = headers;
1263                         
1264                 }
1265
1266                 internal bool IsCached {
1267                         get { return cached_response != null; }
1268                         set {
1269                                 if (value)
1270                                         cached_response = new CachedRawResponse (cache_policy);
1271                                 else
1272                                         cached_response = null;
1273                         }
1274                 }
1275
1276                 public HttpCachePolicy Cache {
1277                         get {
1278                                 if (cache_policy == null)
1279                                         cache_policy = new HttpCachePolicy ();
1280                                 
1281                                 return cache_policy;
1282                         }
1283                 }               
1284
1285                 internal CachedRawResponse GetCachedResponse ()
1286                 {
1287                         if (cached_response != null) {
1288                                 cached_response.StatusCode = StatusCode;
1289                                 cached_response.StatusDescription = StatusDescription;
1290                         }
1291                         
1292                         return cached_response;
1293                 }
1294
1295                 //
1296                 // This is one of the old ASP compatibility methods, the real cache
1297                 // control is in the Cache property, and this is a second class citizen
1298                 //
1299                 public string CacheControl {
1300                         set {
1301                                 if (value == null || value == "") {
1302                                         Cache.SetCacheability (HttpCacheability.NoCache);
1303                                         user_cache_control = null;
1304                                 } else if (String.Compare (value, "public", true, Helpers.InvariantCulture) == 0) {
1305                                         Cache.SetCacheability (HttpCacheability.Public);
1306                                         user_cache_control = "public";
1307                                 } else if (String.Compare (value, "private", true, Helpers.InvariantCulture) == 0) {
1308                                         Cache.SetCacheability (HttpCacheability.Private);
1309                                         user_cache_control = "private";
1310                                 } else if (String.Compare (value, "no-cache", true, Helpers.InvariantCulture) == 0) {
1311                                         Cache.SetCacheability (HttpCacheability.NoCache);
1312                                         user_cache_control = "no-cache";
1313                                 } else
1314                                         throw new ArgumentException ("CacheControl property only allows `public', " +
1315                                                                      "`private' or no-cache, for different uses, use " +
1316                                                                      "Response.AppendHeader");
1317                         }
1318
1319                         get { return (user_cache_control != null) ? user_cache_control : "private"; }
1320                 }
1321 #endregion
1322
1323                 internal int GetOutputByteCount ()
1324                 {
1325                         return output_stream.GetTotalLength ();
1326                 }
1327
1328                 internal void ReleaseResources ()
1329                 {
1330                         if (output_stream != null)
1331                                 output_stream.ReleaseResources (true);
1332                         if (completed)
1333                                 return;
1334                         
1335                         Close ();
1336                         completed = true;
1337                 }
1338         }
1339
1340         static class FlagEnd
1341         {
1342                 public static readonly object Value = new object ();
1343         }
1344 }
1345