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