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