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