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