2004-10-03 Ben Maurer <bmaurer@ximian.com>
[mono.git] / mcs / class / System.Web / System.Web / HttpResponse.cs
1 // 
2 // System.Web.HttpResponse
3 //
4 // Authors:
5 //      Patrik Torstensson (Patrik.Torstensson@labs2.com)
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //
8 // (c) 2002 Ximian, Inc. (http://www.ximian.com)
9 //
10
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 using System;
32 using System.Collections;
33 using System.Globalization;
34 using System.IO;
35 using System.Text;
36 using System.Threading;
37 using System.Web.Util;
38 using System.Web.Caching;
39
40 namespace System.Web
41 {
42         public sealed class HttpResponse
43         {
44                 // Chunked encoding static helpers
45                 static byte [] s_arrChunkSuffix = {13, 10};
46                 static byte [] s_arrChunkEnd = {48, 13, 10, 13, 10};
47                 static string s_sChunkedPrefix = "\r\n";
48
49                 ArrayList _Headers;
50                         
51                 bool _bClientDisconnected;
52                 bool _bSuppressHeaders;
53                 bool _bSuppressContent;
54                 bool _bChunked;
55                 bool _bEnded;
56                 bool _bBuffering;
57                 bool _bHeadersSent;
58                 bool _bFlushing;
59                 bool filtered;
60                 long _lContentLength;
61                 int _iStatusCode;
62                 
63                 int _expiresInMinutes;
64                 bool _expiresInMinutesSet;
65                 DateTime _expiresAbsolute;
66                 bool _expiresAbsoluteSet;
67
68                 bool _ClientDisconnected;
69                 bool closed;
70
71                 string  _sContentType;
72                 string  _sCacheControl;
73                 string  _sTransferEncoding;
74                 string  _sCharset;
75                 string  _sStatusDescription;
76
77                 HttpCookieCollection _Cookies;
78                 HttpCachePolicy _CachePolicy;
79
80                 Encoding _ContentEncoding;
81                         
82                 HttpContext _Context;
83                 HttpWriter _Writer;
84                 TextWriter _TextWriter;
85
86                 HttpWorkerRequest _WorkerRequest;
87
88                 ArrayList fileDependencies;
89                 CachedRawResponse cached_response;
90                 ArrayList cached_headers;
91 #if NET_1_1
92                 string redirectLocation;
93 #endif
94
95                 string app_path_mod = null;
96                 
97                 public HttpResponse (TextWriter output)
98                 {
99                          _bBuffering = true;
100                          _bFlushing = false;
101                          _bHeadersSent = false;
102
103                          _Headers = new ArrayList ();
104
105                          _sContentType = "text/html";
106
107                          _iStatusCode = 200;
108                          _sCharset = null;
109                          _sCacheControl = null;
110
111                          _lContentLength = 0;
112                          _bSuppressContent = false;
113                          _bSuppressHeaders = false;
114                          _bClientDisconnected = false;
115
116                          _bChunked = false;
117
118                          _TextWriter = output;
119                 }
120
121                 internal HttpResponse (HttpWorkerRequest WorkerRequest, HttpContext Context)
122                 {
123                          _Context = Context;
124                          _WorkerRequest = WorkerRequest;
125
126                          _bBuffering = true;
127                          _bFlushing = false;
128                          _bHeadersSent = false;
129
130                          _Headers = new ArrayList ();
131
132                          _sContentType = "text/html";
133
134                          _iStatusCode = 200;
135                          _sCharset = null;
136                          _sCacheControl = null;
137
138                          _lContentLength = 0;
139                          _bSuppressContent = false;
140                          _bSuppressHeaders = false;
141                          _bClientDisconnected = false;
142
143                          _bChunked = false;
144                 }
145
146                 internal void InitializeWriter ()
147                 {
148                         // We cannot do this in the .ctor because HttpWriter uses configuration and
149                         // it may not be initialized
150                         if (_Writer == null) {
151                                  _Writer = new HttpWriter (this);
152                                  _TextWriter = _Writer;
153                         }
154                 }
155                 
156                 internal void FinalFlush ()
157                 {
158                         Flush (true);
159                 }
160
161                 internal void DoFilter (bool really)
162                 {
163                         if (really && null != _Writer) 
164                                 _Writer.FilterData (true);
165
166                         filtered = true;
167                 }
168
169                 internal bool IsCached {
170                         get { return cached_response != null; }
171                 }
172
173                 internal CachedRawResponse GetCachedResponse () {
174                         cached_response.StatusCode = StatusCode;
175                         cached_response.StatusDescription = StatusDescription;
176                         return cached_response;
177                 }
178
179                 internal void SetCachedHeaders (ArrayList headers)
180                 {
181                         cached_headers = headers;
182                 }
183                 
184                 private ArrayList GenerateHeaders ()
185                 {
186                         ArrayList oHeaders = new ArrayList (_Headers.ToArray ());
187
188                         oHeaders.Add (new HttpResponseHeader ("X-Powered-By", "Mono"));
189                         // save culture info, we need us info here
190                         CultureInfo oSavedInfo = Thread.CurrentThread.CurrentCulture;
191                         Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
192
193                         string date = DateTime.UtcNow.ToString ("ddd, d MMM yyyy HH:mm:ss ");
194                         HttpResponseHeader date_header = new HttpResponseHeader ("Date", date + "GMT");
195                         oHeaders.Add (date_header);
196                         
197                         if (IsCached)
198                                 cached_response.DateHeader = date_header;
199
200                         Thread.CurrentThread.CurrentCulture = oSavedInfo;
201
202                         if (_lContentLength > 0) {
203                                 oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderContentLength,
204                                                                       _lContentLength.ToString ()));
205                         }
206
207                         if (_sContentType != null) {
208                                 if (_sContentType.IndexOf ("charset=") == -1) {
209                                         if (Charset.Length == 0) {
210                                                 Charset = ContentEncoding.HeaderName;
211                                         }
212
213                                         // Time to build our string
214                                         if (Charset.Length > 0) {
215                                                 _sContentType += "; charset=" + Charset;
216                                         }
217                                 }
218
219                                 oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderContentType,
220                                                                       _sContentType));
221                         }
222
223                         if (_CachePolicy != null)
224                                 _CachePolicy.SetHeaders (this, oHeaders);
225                         
226                         if (_sCacheControl != null) {
227                                 oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderPragma,
228                                                                       _sCacheControl));
229                         }
230
231                         if (_sTransferEncoding != null) {
232                                 oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderTransferEncoding,
233                                                                       _sTransferEncoding));
234                         }
235
236                         if (_Cookies != null) {
237                                 int length = _Cookies.Count;
238                                 for (int i = 0; i < length; i++) {
239                                         oHeaders.Add (_Cookies.Get (i).GetCookieHeader ());
240                                 }
241                         }
242 #if NET_1_1
243                         if (redirectLocation != null)
244                                 oHeaders.Add (new HttpResponseHeader (HttpWorkerRequest.HeaderLocation,
245                                                                       redirectLocation));
246 #endif
247                         return oHeaders;
248                 }
249                 
250                 private void SendHeaders ()
251                 {
252                         _WorkerRequest.SendStatus (StatusCode, StatusDescription);
253                         
254                         ArrayList oHeaders;
255
256                         if (cached_headers != null)
257                                 oHeaders = cached_headers;
258                         else
259                                 oHeaders = GenerateHeaders ();
260
261                         if (cached_response != null)
262                                 cached_response.SetHeaders (oHeaders);
263                         
264                         foreach (HttpResponseHeader oHeader in oHeaders)
265                                 oHeader.SendContent (_WorkerRequest);
266                         
267                         _bHeadersSent = true;
268                 }
269
270                 public string Status
271                 {
272                         get {
273                                 return String.Format ("{0} {1}", StatusCode, StatusDescription);
274                         }
275
276                         set {
277                                 string sMsg = "OK";
278                                 int iCode = 200;
279
280                                 try {
281                                         iCode = Int32.Parse (value.Substring (0, value.IndexOf (' ')));
282                                         sMsg = value.Substring (value.IndexOf (' ') + 1);
283                                 } catch (Exception) {
284                                         throw new HttpException ("Invalid status string");
285                                 }
286
287                                 StatusCode = iCode;
288                                 StatusDescription = sMsg;
289                         }
290                 }
291
292                 [MonoTODO()]
293                 public void AddCacheItemDependencies (ArrayList cacheKeys)
294                 {
295                         throw new NotImplementedException ();
296                 }
297
298                 [MonoTODO()]
299                 public void AddCacheItemDependency(string cacheKey)
300                 {
301                         throw new NotImplementedException ();
302                 }
303
304                 public void AddFileDependencies (ArrayList filenames)
305                 {
306                         if (filenames == null || filenames.Count == 0)
307                                 return;
308                         
309                         if (fileDependencies == null) {
310                                 fileDependencies = (ArrayList) filenames.Clone ();
311                                 return;
312                         }
313
314                         foreach (string fn in filenames)
315                                 AddFileDependency (fn);
316                 }
317
318                 public void AddFileDependency (string filename)
319                 {
320                         if (fileDependencies == null)
321                                 fileDependencies = new ArrayList ();
322
323                         fileDependencies.Add (filename);
324                 }
325
326                 public void AddHeader (string name, string value)
327                 {
328                         AppendHeader(name, value);
329                 }
330
331                 public void AppendCookie (HttpCookie cookie)
332                 {
333                         if (_bHeadersSent)
334                                 throw new HttpException ("Cannot append cookies after HTTP headers have been sent");
335
336                         Cookies.Add (cookie);
337                 }
338
339                 [MonoTODO()]
340                 public void AppendToLog (string param)
341                 {
342                         throw new NotImplementedException ();
343                 }
344
345                 public string ApplyAppPathModifier (string virtualPath)
346                 {
347                         if (virtualPath == null)
348                                 return null;
349
350                         if (virtualPath == "")
351                                 return _Context.Request.RootVirtualDir;
352
353                         if (UrlUtils.IsRelativeUrl (virtualPath)) {
354                                 virtualPath = UrlUtils.Combine (_Context.Request.RootVirtualDir, virtualPath);
355                         } else if (UrlUtils.IsRooted (virtualPath)) {
356                                 virtualPath = UrlUtils.Reduce (virtualPath);
357                         }
358
359                         if (app_path_mod != null && virtualPath.IndexOf (app_path_mod) > 0)
360                                 virtualPath = UrlUtils.Combine (app_path_mod, virtualPath);
361
362                         return virtualPath;
363                 }
364
365                 internal void SetAppPathModifier (string app_path_mod)
366                 {
367                         this.app_path_mod = app_path_mod;
368                 }
369                 
370                 public bool Buffer
371                 {
372                         get {
373                                 return BufferOutput;
374                         }
375
376                         set {
377                                 BufferOutput = value;
378                         }
379                 }
380
381                 public bool BufferOutput
382                 {
383                         get {
384                                 return _bBuffering;
385                         }
386                         
387                         set {
388                                 if (_Writer != null)
389                                         _Writer.Update ();
390
391                                 _bBuffering = value;
392                         }
393                 }
394
395                 public HttpCachePolicy Cache
396                 {
397                         get {
398                                 if (null == _CachePolicy) {
399                                         _CachePolicy = new HttpCachePolicy ();
400                                         _CachePolicy.CacheabilityUpdated += new CacheabilityUpdatedCallback (
401                                                 OnCacheabilityUpdated);
402                                 }
403
404                                 return _CachePolicy;
405                         }
406                 }
407
408                 private void OnCacheabilityUpdated (object sender, CacheabilityUpdatedEventArgs e)
409                 {
410                         if (e.Cacheability >= HttpCacheability.Server && !IsCached)
411                                 cached_response = new CachedRawResponse (_CachePolicy);
412                         else if (e.Cacheability <= HttpCacheability.Private)
413                                 cached_response = null;
414                 }
415                 
416                 [MonoTODO("Set status in the cache policy")]
417                 public string CacheControl
418                 {
419                         get {
420                                 return _sCacheControl;
421                         }
422
423                         set {
424                                 if (_bHeadersSent)
425                                         throw new HttpException ("Headers has been sent to the client");
426
427                                 _sCacheControl = value;
428                         }
429                 }
430
431                 public string Charset
432                 {
433                         get {
434                                 if (null == _sCharset)
435                                         _sCharset = ContentEncoding.WebName;
436
437                                 return _sCharset;
438                         }
439
440                         set {
441                                 if (_bHeadersSent)
442                                         throw new HttpException ("Headers has been sent to the client");
443
444                                 _sCharset = value;
445                         }
446                 }
447
448                 public Encoding ContentEncoding
449                 {
450                         get {
451                                 if (_ContentEncoding == null)
452                                         _ContentEncoding = WebEncoding.ResponseEncoding;
453
454                                 return _ContentEncoding;
455                         }
456
457                         set {
458                                 if (value == null)
459                                         throw new ArgumentNullException ("Can't set a null as encoding");
460
461                                 _ContentEncoding = value;
462
463                                 if (_Writer != null)
464                                         _Writer.Update ();
465                         }
466                 }
467
468                 public string ContentType
469                 {
470                         get {
471                                 return _sContentType;
472                         }
473
474                         set {
475                                 if (_bHeadersSent)
476                                         throw new HttpException ("Headers has been sent to the client");
477
478                                 _sContentType = value;
479                         }
480                 }
481
482                 public HttpCookieCollection Cookies
483                 {
484                         get {
485                                 if (null == _Cookies)
486                                         _Cookies = new HttpCookieCollection (this, false);
487
488                                 return _Cookies;
489                         }
490                 }
491
492                 public int Expires
493                 {
494                         get {
495                                 return _expiresInMinutes;
496                         }
497
498                         set {
499                                 if (!_expiresInMinutesSet || (value < _expiresInMinutes))\r
500                                 {\r
501                                         _expiresInMinutes = value;\r
502                                         Cache.SetExpires(_Context.Timestamp.Add(new TimeSpan(0, _expiresInMinutes, 0)));\r
503                                 }
504                                 _expiresInMinutesSet = true;
505                         }
506                 }
507
508                 public DateTime ExpiresAbsolute
509                 {
510                         get {
511                                 return _expiresAbsolute;
512                         }
513
514                         set {
515                                 if (!_expiresAbsoluteSet || value.CompareTo(_expiresAbsolute)<0)\r
516                                 {\r
517                                         _expiresAbsolute = value;\r
518                                         Cache.SetExpires(_expiresAbsolute); \r
519                                 }
520                                 _expiresAbsoluteSet = true;
521                         }
522                 }
523
524                 public Stream Filter
525                 {
526                         get {
527                                 if (_Writer != null)
528                                         return _Writer.GetActiveFilter ();
529
530                                 return null;
531                         }
532
533                         set {
534                                 if (_Writer == null)
535                                         throw new HttpException ("Filtering is not allowed");
536
537                                 _Writer.ActivateFilter (value);
538                         }
539                 }
540
541                 public bool IsClientConnected
542                 {
543                         get {
544                                 if (_ClientDisconnected)
545                                         return false;
546
547                                 if (null != _WorkerRequest && (!_WorkerRequest.IsClientConnected ())) {
548                                         _ClientDisconnected = false;
549                                         return false;
550                                 }
551
552                                 return true;
553                         }
554                 }
555       
556                 public TextWriter Output
557                 {
558                         get {
559                                 return _TextWriter;
560                         }
561                 }
562
563                 public Stream OutputStream
564                 {
565                         get {
566                                 if (_Writer == null)
567                                         throw new HttpException ("an Output stream not available when " +
568                                                                  "running with custom text writer");
569
570                                 return _Writer.OutputStream;
571                         }
572                 }
573
574 #if NET_1_1
575                 public string RedirectLocation {
576                         get { return redirectLocation; }
577                         set { redirectLocation = value; }
578                 }
579 #endif
580                 
581                 public string StatusDescription
582                 {
583                         get {
584                                 if (null == _sStatusDescription)
585                                         _sStatusDescription =
586                                                 HttpWorkerRequest.GetStatusDescription (_iStatusCode);
587
588                                 return _sStatusDescription;
589                         }
590
591                         set {
592                                 if (_bHeadersSent)
593                                         throw new HttpException ("Headers has been sent to the client");
594
595                                 _sStatusDescription = value;
596                         }
597                 }
598         
599                 public int StatusCode
600                 {
601                         get {
602                                 return _iStatusCode;
603                         }
604
605                         set {
606                                 if (_bHeadersSent)
607                                         throw new HttpException ("Headers has been sent to the client");
608
609                                 _sStatusDescription = null;
610                                 _iStatusCode = value;
611                         }
612                 }
613
614                 public bool SuppressContent
615                 {
616                         get {
617                                 return _bSuppressContent;
618                         }
619                         
620                         set {
621                                 _bSuppressContent = true;
622                         }
623                 }
624
625                 HttpRequest Request
626                 {
627                         get {
628                                 if (_Context == null)
629                                         return null;
630
631                                 return _Context.Request;
632                         }
633                 }
634
635                 internal void AppendHeader (int iIndex, string value)
636                 {
637                         if (_bHeadersSent)
638                                 throw new HttpException ("Headers has been sent to the client");
639
640                         switch (iIndex) {
641                         case HttpWorkerRequest.HeaderContentLength:
642                                 _lContentLength = Int64.Parse (value);
643                                 break;
644                         case HttpWorkerRequest.HeaderContentEncoding:
645                                 _sContentType = value;
646                                 break;
647                         case HttpWorkerRequest.HeaderTransferEncoding:
648                                 _sTransferEncoding = value;
649                                 _bChunked = (value == "chunked");
650                                 break;
651                         case HttpWorkerRequest.HeaderPragma:
652                                 _sCacheControl = value;
653                                 break;
654                         default:
655                                 _Headers.Add (new HttpResponseHeader (iIndex, value));
656                                 break;
657                         }
658                 }
659
660                 public void AppendHeader (string name, string value)
661                 {
662                         if (_bHeadersSent)
663                                 throw new HttpException ("Headers has been sent to the client");
664
665                         switch (name.ToLower ()) {
666                         case "content-length":
667                                 _lContentLength = Int64.Parse (value);
668                                 break;
669                         case "content-type":
670                                 _sContentType = value;
671                                 break;
672                         case "transfer-encoding":
673                                 _sTransferEncoding = value;
674                                 _bChunked = (value == "chunked");
675                                 break;
676                         case "pragma":
677                                 _sCacheControl = value;
678                                 break;
679                         default:
680                                 _Headers.Add (new HttpResponseHeader (name, value));
681                                 break;
682                         }
683                 }
684         
685                 internal TextWriter SetTextWriter (TextWriter w)
686                 {
687                         TextWriter prev = _TextWriter;
688                         _TextWriter = w;
689                         return prev;
690                 }
691                 
692                 public void BinaryWrite (byte [] buffer)
693                 {
694                         OutputStream.Write (buffer, 0, buffer.Length);
695                 }
696
697                 internal void BinaryWrite (byte [] buffer, int start, int length)
698                 {
699                         OutputStream.Write (buffer, start, length);
700                 }
701                 
702                 public void Clear ()
703                 {
704                         if (_Writer != null)
705                                 _Writer.Clear ();
706                 }
707
708                 public void ClearContent ()
709                 {
710                         Clear();
711                 }
712
713                 public void ClearHeaders ()
714                 {
715                         if (_bHeadersSent)
716                                 throw new HttpException ("Headers has been sent to the client");
717
718                         _sContentType = "text/html";
719
720                         _iStatusCode = 200;
721                         _sCharset = null;
722                         _Headers = new ArrayList ();
723                         _sCacheControl = null;
724                         _sTransferEncoding = null;
725
726                         _lContentLength = 0;
727                         _bSuppressContent = false;
728                         _bSuppressHeaders = false;
729                         _bClientDisconnected = false;
730                 }
731
732                 public void Close ()
733                 {
734                         if (closed && !_bClientDisconnected) {
735                                 _bClientDisconnected = false;
736                                 _WorkerRequest.CloseConnection ();
737                                 _bClientDisconnected = true;
738                         }
739                 }
740
741                 internal void Dispose ()
742                 {
743                         if (_Writer != null) {
744                                 _Writer.Dispose ();
745                                 _Writer = null;
746                         }
747                 }
748
749                 [MonoTODO("Handle callbacks into before done with session, needs to have a non ended flush here")]
750                 internal void FlushAtEndOfRequest () 
751                 {
752                         Flush (true);
753                 }
754
755                 public void End ()
756                 {
757                         if (_bEnded)
758                                 return;
759
760                         if (_Context.TimeoutPossible)
761                                 Thread.CurrentThread.Abort (new StepCompleteRequest ());
762
763                         Flush ();
764                         _bEnded = true;
765                         _Context.ApplicationInstance.CompleteRequest ();
766                 }
767
768                 public void Flush ()
769                 {
770                         if (closed)
771                                 throw new HttpException ("Response already finished.");
772
773                         Flush (false);
774                 }
775
776                 private void Flush (bool bFinish)
777                 {
778                         if (_bFlushing || closed)
779                                 return;
780
781                         _bFlushing = true;
782
783                         if (_Writer == null) {
784                                 _TextWriter.Flush ();
785                                 _bFlushing = false;
786                                 return;
787                         }
788
789                         try {
790                                 if (_bClientDisconnected)
791                                         return;
792
793                                 long length = _Writer.BufferSize;
794                                 if (!_bHeadersSent && !_bSuppressHeaders) {
795                                         if (bFinish) {
796                                                 if (length == 0 && _lContentLength == 0)
797                                                         _sContentType = null;
798
799                                                 SendHeaders ();
800                                                 length = _Writer.BufferSize;
801                                                 if (length != 0)
802                                                         _WorkerRequest.SendCalculatedContentLength ((int) length);
803                                         } else {
804                                                 if (_lContentLength == 0 && _iStatusCode == 200 &&
805                                                    _sTransferEncoding == null) {
806                                                         // Check we are going todo chunked encoding
807                                                         string sProto = Request.ServerVariables ["SERVER_PROTOCOL"];
808                                                         if (sProto != null && sProto == "HTTP/1.1") {
809                                                                 AppendHeader (
810                                                                         HttpWorkerRequest.HeaderTransferEncoding,
811                                                                         "chunked");
812                                                         }  else {
813                                                                 // Just in case, the old browsers send a HTTP/1.0
814                                                                 // request with Connection: Keep-Alive
815                                                                 AppendHeader (
816                                                                         HttpWorkerRequest.HeaderConnection,
817                                                                         "Close");
818                                                         }
819                                                 }
820
821                                                 length = _Writer.BufferSize;
822                                                 SendHeaders ();
823                                         }
824                                 }
825
826                                 if (!filtered) {
827                                         _Writer.FilterData (false);
828                                         length = _Writer.BufferSize;
829                                 }
830
831                                 if (length == 0) {
832                                         if (bFinish && _bChunked) {
833                                                 _WorkerRequest.SendResponseFromMemory (s_arrChunkEnd,
834                                                                                 s_arrChunkEnd.Length);
835                                         }
836
837                                         _WorkerRequest.FlushResponse (bFinish);
838                                         if (!bFinish)
839                                                 _Writer.Clear ();
840                                         return;
841                                 }
842
843                                 if (!_bSuppressContent && Request.HttpMethod == "HEAD")
844                                         _bSuppressContent = true;
845
846                                 if (_bSuppressContent)
847                                         _Writer.Clear ();
848
849                                 if (!_bSuppressContent) {
850                                         _bClientDisconnected = false;
851                                         if (_bChunked) {
852                                                 Encoding oASCII = Encoding.ASCII;
853
854                                                 string chunk = Convert.ToString(_Writer.BufferSize, 16);
855                                                 byte [] arrPrefix = oASCII.GetBytes (chunk + s_sChunkedPrefix);
856
857                                                 _WorkerRequest.SendResponseFromMemory (arrPrefix,
858                                                                                        arrPrefix.Length);
859
860                                                 _Writer.SendContent (_WorkerRequest);
861
862                                                 _WorkerRequest.SendResponseFromMemory (s_arrChunkSuffix,
863                                                                                        s_arrChunkSuffix.Length);
864                                                 if (bFinish)
865                                                         _WorkerRequest.SendResponseFromMemory (
866                                                                         s_arrChunkEnd, s_arrChunkEnd.Length);
867                                         } else {
868                                                 _Writer.SendContent (_WorkerRequest);
869                                         }
870                                 }
871
872                                 _WorkerRequest.FlushResponse (bFinish);
873                                 if (IsCached) {
874                                         cached_response.ContentLength = (int) length;
875                                         cached_response.SetData (_Writer.GetBuffer ());
876                                 }
877                                 _Writer.Clear ();
878                         } finally {
879                                 if (bFinish)
880                                         closed = true;
881                                 _bFlushing = false;
882                         }
883                 }
884
885                 public void Pics (string value)
886                 {
887                         AppendHeader ("PICS-Label", value);
888                 }
889
890
891                 public void Redirect (string url)
892                 {
893                         Redirect (url, true);
894                 }
895
896                 public void Redirect (string url, bool endResponse)
897                 {
898                         if (_bHeadersSent)
899                                 throw new HttpException ("Headers has been sent to the client");
900
901                         Clear ();
902
903                         url = ApplyAppPathModifier (url);
904                         StatusCode = 302;
905                         AppendHeader (HttpWorkerRequest.HeaderLocation, url);
906
907                         // Text for browsers that can't handle location header
908                         Write ("<html><head><title>Object moved</title></head><body>\r\n");
909                         Write ("<h2>Object moved to <a href='" + url + "'>here</a></h2>\r\n");
910                         Write ("</body><html>\r\n");
911
912                         if (endResponse)
913                                 End ();
914                 }
915
916                 internal bool RedirectCustomError (string errorPage)
917                 {
918                         if (_bHeadersSent)
919                                 return false;
920
921                         if (Request.QueryString ["aspxerrorpath"] != null)
922                                 return false; // Prevent endless loop
923
924                         Redirect (errorPage + "?aspxerrorpath=" + Request.Path, false);
925                         return true;
926                 }
927                 
928                 public void Write (char ch)
929                 {
930                         _TextWriter.Write(ch);
931                 }
932
933                 public void Write (object obj)
934                 {
935                         _TextWriter.Write(obj);
936                 }
937
938                 public void Write (string str)
939                 {
940                         _TextWriter.Write (str);
941                 }
942
943                 public void Write (char [] buffer, int index, int count)
944                 {
945                         _TextWriter.Write (buffer, index, count);
946                 }
947
948                 public static void RemoveOutputCacheItem (string path)
949                 {
950                         if (path == null)
951                                 throw new ArgumentNullException ("path");
952                         
953                         if (!UrlUtils.IsRooted (path))
954                                 throw new ArgumentException ("Invalid path for HttpResponse.RemoveOutputCacheItem '" +
955                                         path + "'. An absolute virtual path is expected.");
956
957                         Cache cache = HttpRuntime.Cache;
958                         cache.Remove (path);
959                 }
960
961                 public void SetCookie (HttpCookie cookie)
962                 {
963                         if (_bHeadersSent)
964                                 throw new HttpException ("Cannot append cookies after HTTP headers have been sent");
965
966                         Cookies.Add (cookie);
967                 }
968
969                 private void WriteFromStream (Stream stream, long offset, long length, long bufsize)
970                 {
971                         if (offset < 0 || length <= 0)
972                                 return;
973                         
974                         long stLength = stream.Length;
975                         if (offset + length > stLength)
976                                 length = stLength - offset;
977
978                         if (offset > 0)
979                                 stream.Seek (offset, SeekOrigin.Begin);
980
981                         byte [] fileContent = new byte [bufsize];
982                         int count = (int) Math.Min (Int32.MaxValue, bufsize);
983                         while (length > 0 && (count = stream.Read (fileContent, 0, count)) != 0) {
984                                 _Writer.WriteBytes (fileContent, 0, count);
985                                 length -= count;
986                                 count = (int) Math.Min (length, fileContent.Length);
987                         }
988                 }
989
990                 public void WriteFile (string filename)
991                 {
992                         WriteFile (filename, false);
993                 }
994
995                 public void WriteFile (string filename, bool readIntoMemory)
996                 {
997                         FileStream fs = null;
998                         try {
999                                 fs = File.OpenRead (filename);
1000                                 long size = fs.Length;
1001                                 if (readIntoMemory) {
1002                                         WriteFromStream (fs, 0, size, size);
1003                                 } else {
1004                                         WriteFromStream (fs, 0, size, 8192);
1005                                 }
1006                         } finally {
1007                                 if (fs != null)
1008                                         fs.Close ();
1009                         }
1010                 }
1011
1012                 public void WriteFile (string filename, long offset, long size)
1013                 {
1014                         FileStream fs = null;
1015                         try {
1016                                 fs = File.OpenRead (filename);
1017                                 WriteFromStream (fs, offset, size, 8192);
1018                         } finally {
1019                                 if (fs != null)
1020                                         fs.Close ();
1021                         }
1022                 }
1023
1024                 public void WriteFile (IntPtr fileHandle, long offset, long size)
1025                 {
1026                         FileStream fs = null;
1027                         try {
1028                                 fs = new FileStream (fileHandle, FileAccess.Read);
1029                                 WriteFromStream (fs, offset, size, 8192);
1030                         } finally {
1031                                 if (fs != null)
1032                                         fs.Close ();
1033                         }
1034                 }   
1035
1036                 [MonoTODO()]
1037                 internal void OnCookieAdd (HttpCookie cookie)
1038                 {
1039                 }
1040
1041                 [MonoTODO("Do we need this?")]
1042                 internal void OnCookieChange (HttpCookie cookie)
1043                 {
1044                 }
1045
1046                 [MonoTODO()]
1047                 internal void GoingToChangeCookieColl ()
1048                 {
1049                 }
1050
1051                 [MonoTODO()]
1052                 internal void ChangedCookieColl ()
1053                 {
1054                 }
1055         }
1056 }