Merge pull request #1304 from slluis/mac-proxy-autoconfig
[mono.git] / mcs / class / System.Web / System.Web / HttpResponseStream.cs
1 //
2 // System.Web.HttpResponseStream.cs 
3 //
4 // 
5 // Author:
6 //      Miguel de Icaza (miguel@novell.com)
7 //      Ben Maurer (bmaurer@ximian.com)
8 //
9 //
10 // Copyright (C) 2005-2009 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.IO;
34 using System.Text;
35 using System.Globalization;
36 using System.Runtime.InteropServices;
37         
38 namespace System.Web
39 {
40         //
41         // HttpResponseStream implements the "OutputStream" from HttpResponse
42         //
43         // The MS implementation is broken in that it does not hook up this
44         // to HttpResponse, so calling "Flush" on Response.OutputStream does not
45         // flush the contents and produce the headers.
46         //
47         // You must call HttpResponse.Flush which does the actual header generation
48         // and actual data flushing
49         //
50         class HttpResponseStream : Stream
51         {
52                 Bucket first_bucket;
53                 Bucket cur_bucket;
54                 HttpResponse response;
55                 internal long total;
56                 Stream filter;
57                 byte [] chunk_buffer = new byte [24];
58
59                 public HttpResponseStream (HttpResponse response)
60                 {
61                         this.response = response;
62                 }
63
64                 internal bool HaveFilter {
65                         get { return filter != null; }
66                 }
67
68                 public Stream Filter {
69                         get {
70                                 if (filter == null)
71                                         filter = new OutputFilterStream (this);
72                                 return filter;
73                         }
74                         set {
75                                 filter = value;
76                         }
77                 }
78                 unsafe sealed class BlockManager {
79                         const int PreferredLength = 128 * 1024;
80                         byte *data;
81                         int position;
82                         int block_size;
83
84                         public BlockManager ()
85                         {
86                         }
87
88                         public int Position {
89                                 get { return position; }
90                         }
91
92                         void EnsureCapacity (int capacity)
93                         {
94                                 if (block_size >= capacity)
95                                         return;
96
97                                 capacity += PreferredLength;
98                                 capacity = (capacity / PreferredLength) * PreferredLength;
99
100                                 data = data == null
101                                         ? (byte *) Marshal.AllocHGlobal (capacity)
102                                         : (byte *) Marshal.ReAllocHGlobal ((IntPtr) data, (IntPtr) capacity);
103                                 block_size = capacity;
104                         }
105
106                         public void Write (byte [] buffer, int offset, int count)
107                         {
108                                 if (count == 0)
109                                         return;
110                                 
111                                 EnsureCapacity (position + count);
112                                 Marshal.Copy (buffer, offset, (IntPtr) (data + position), count);
113                                 position += count;
114                         }
115
116                         public void Write (IntPtr ptr, int count)
117                         {
118                                 if (count == 0)
119                                         return;
120                                 
121                                 EnsureCapacity (position + count);
122                                 byte *src = (byte *) ptr.ToPointer ();
123                                 if (count < 32) {
124                                         byte *dest = (data + position);
125                                         for (int i = 0; i < count; i++)
126                                                 *dest++ = *src++;
127                                 } else {
128                                         memcpy (data + position, src, count);
129                                 }
130                                 position += count;
131                         }
132
133                         public void Send (HttpWorkerRequest wr, int start, int end)
134                         {
135                                 if (end - start <= 0)
136                                         return;
137
138                                 wr.SendResponseFromMemory ((IntPtr) (data + start), end - start);
139                         }
140
141                         public void Send (Stream stream, int start, int end)
142                         {
143                                 int len = end - start;
144                                 if (len <= 0)
145                                         return;
146
147                                 byte [] buffer = new byte [Math.Min (len, 32 * 1024)];
148                                 int size = buffer.Length;
149                                 while (len > 0) {
150                                         Marshal.Copy ((IntPtr) (data + start), buffer, 0, size);
151                                         stream.Write (buffer, 0, size);
152                                         start += size;
153                                         len -= size;
154                                         if (len > 0 && len < size)
155                                                 size = len;
156                                 }
157                         }
158                         
159                         public void Dispose ()
160                         {
161                                 if ((IntPtr) data != IntPtr.Zero) {
162                                         Marshal.FreeHGlobal ((IntPtr) data);
163                                         data = (byte *) IntPtr.Zero;
164                                 }
165                         }
166                 }
167
168                 abstract class Bucket {
169                         public Bucket Next;
170
171                         public virtual void Dispose ()
172                         {
173                         }
174
175                         public abstract void Send (HttpWorkerRequest wr);
176                         public abstract void Send (Stream stream);
177                         public abstract int Length { get; }
178                 }
179
180                 unsafe
181                 class ByteBucket : Bucket {
182                         int start;
183                         int length;
184                         public BlockManager blocks;
185                         public bool Expandable = true;
186
187                         public ByteBucket () : this (null)
188                         {
189                         }
190
191                         public ByteBucket (BlockManager blocks)
192                         {
193                                 if (blocks == null)
194                                         blocks = new BlockManager ();
195
196                                 this.blocks = blocks;
197                                 start = blocks.Position;
198                         }
199
200                         public override int Length {
201                                 get { return length; }
202                         }
203
204                         public unsafe int Write (byte [] buf, int offset, int count)
205                         {
206                                 if (Expandable == false)
207                                         throw new Exception ("This should not happen.");
208
209                                 fixed (byte *p = &buf[0]) {
210                                         IntPtr p2 = new IntPtr (p + offset);
211                                         blocks.Write (p2, count);
212                                 }
213
214                                 length += count;
215                                 return count;
216                         }
217
218                         public int Write (IntPtr ptr, int count)
219                         {
220                                 if (Expandable == false)
221                                         throw new Exception ("This should not happen.");
222
223                                 blocks.Write (ptr, count);
224                                 length += count;
225                                 return count;
226                         }
227
228                         public override void Dispose ()
229                         {
230                                 blocks.Dispose ();
231                         }
232
233                         public override void Send (HttpWorkerRequest wr)
234                         {
235                                 if (length == 0)
236                                         return;
237
238                                 blocks.Send (wr, start, length);
239                         }
240
241                         public override void Send (Stream stream)
242                         {
243                                 if (length == 0)
244                                         return;
245
246                                 blocks.Send (stream, start, length);
247                         }
248                 }
249
250                 class BufferedFileBucket : Bucket {
251                         string file;
252                         long offset;
253                         long length;
254         
255                         public BufferedFileBucket (string f, long off, long len)
256                         {
257                                 file = f;
258                                 offset = off;
259                                 length = len;
260                         }
261
262                         public override int Length {
263                                 get { return (int) length; }
264                         }
265
266                         public override void Send (HttpWorkerRequest wr)
267                         {
268                                 wr.SendResponseFromFile (file, offset, length);
269                         }
270
271                         public override void Send (Stream stream)
272                         {
273                                 using (FileStream fs = File.OpenRead (file)) {
274                                         byte [] buffer = new byte [Math.Min (fs.Length, 32*1024)];
275
276                                         long remain = fs.Length;
277                                         int n;
278                                         while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
279                                                 remain -= n;
280                                                 stream.Write (buffer, 0, n);
281                                         }
282                                 }
283                         }
284
285                         public override string ToString ()
286                         {
287                                 return "file " + file + " " + length.ToString () + " bytes from position " + offset.ToString ();
288                         }       
289                 }
290         
291                 void AppendBucket (Bucket b)
292                 {
293                         if (first_bucket == null) {
294                                 cur_bucket = first_bucket = b;
295                                 return;
296                         }
297         
298                         cur_bucket.Next = b;
299                         cur_bucket = b;
300                 }
301         
302                 //
303                 // Nothing happens here, broken by requirement.
304                 // See note at the start
305                 //
306                 public override void Flush () 
307                 {
308                 }
309
310                 void SendChunkSize (long l, bool last)
311                 {
312                         if (l == 0 && !last)
313                                 return;
314
315                         int i = 0;
316                         if (l >= 0) {
317                                 string s = l.ToString ("x");
318                                 for (; i < s.Length; i++)
319                                         chunk_buffer [i] = (byte) s [i];
320                         }
321
322                         chunk_buffer [i++] = 13;
323                         chunk_buffer [i++] = 10;
324                         if (last) {
325                                 chunk_buffer [i++] = 13;
326                                 chunk_buffer [i++] = 10;
327                         }
328
329                         response.WorkerRequest.SendResponseFromMemory (chunk_buffer, i);
330                 }
331
332                 internal void Flush (HttpWorkerRequest wr, bool final_flush)
333                 {
334                         if (total == 0 && !final_flush)
335                                 return;
336
337                         if (response.use_chunked) 
338                                 SendChunkSize (total, false);
339
340                         for (Bucket b = first_bucket; b != null; b = b.Next) {
341                                 b.Send (wr);
342                         }
343
344                         if (response.use_chunked) {
345                                 SendChunkSize (-1, false);
346                                 if (final_flush)
347                                         SendChunkSize (0, true);
348                         }
349
350                         wr.FlushResponse (final_flush);
351
352                         Clear ();
353                 }
354
355                 internal int GetTotalLength ()
356                 {
357                         int size = 0;
358                         for (Bucket b = first_bucket; b != null; b = b.Next)
359                                 size += b.Length;
360
361                         return size;
362                 }
363
364                 internal MemoryStream GetData ()
365                 {
366                         MemoryStream stream = new MemoryStream ();
367                         for (Bucket b = first_bucket; b != null; b = b.Next)
368                                 b.Send (stream);
369                         return stream;
370                 }
371
372                 public void WriteFile (string f, long offset, long length)
373                 {
374                         if (length == 0)
375                                 return;
376
377                         ByteBucket bb = cur_bucket as ByteBucket;
378
379                         if (bb != null) {
380                                 bb.Expandable = false;
381                                 bb = new ByteBucket (bb.blocks);
382                         }
383
384                         total += length;
385                         
386                         AppendBucket (new BufferedFileBucket (f, offset, length));
387                         if (bb != null)
388                                 AppendBucket (bb);
389                         // Flush () is called from HttpResponse if needed (WriteFile/TransmitFile)
390                 }
391
392                 bool filtering;
393                 internal void ApplyFilter (bool close)
394                 {
395                         if (filter == null)
396                                 return;
397
398                         filtering = true;
399                         Bucket one = first_bucket;
400                         first_bucket = null; // This will recreate new buckets for the filtered content
401                         cur_bucket = null;
402                         total = 0;
403                         for (Bucket b = one; b != null; b = b.Next)
404                                 b.Send (filter);
405
406                         for (Bucket b = one; b != null; b = b.Next)
407                                 b.Dispose ();
408
409                         if (close) {
410                                 filter.Flush ();
411                                 filter.Close ();
412                                 filter = null;
413                         } else {
414                                 filter.Flush ();
415                         }
416                         filtering = false;
417                 }
418
419                 public void WritePtr (IntPtr ptr, int length)
420                 {
421                         if (length == 0)
422                                 return;
423
424                         bool buffering = response.BufferOutput;
425
426                         if (buffering) {
427                                 // It does not matter whether we're in ApplyFilter or not
428                                 AppendBuffer (ptr, length);
429                         } else if (filter == null || filtering) {
430                                 response.WriteHeaders (false);
431                                 HttpWorkerRequest wr = response.WorkerRequest;
432                                 // Direct write because not buffering
433                                 wr.SendResponseFromMemory (ptr, length);
434                                 wr.FlushResponse (false);
435                         } else {
436                                 // Write to the filter, which will call us back, and then Flush
437                                 filtering = true;
438                                 try {
439                                         byte [] bytes = new byte [length];
440                                         Marshal.Copy (ptr, bytes, 0, length);
441                                         filter.Write (bytes, 0, length);
442                                         bytes = null;
443                                 } finally {
444                                         filtering = false;
445                                 }
446                                 Flush (response.WorkerRequest, false);
447                         }
448
449                 }
450
451                 public override void Write (byte [] buffer, int offset, int count)
452                 {
453                         bool buffering = response.BufferOutput;
454                         if (buffer == null)
455                                 throw new ArgumentNullException ("buffer");
456
457                         int max_count = buffer.Length - offset;
458                         if (offset < 0 || max_count <= 0)
459                                 throw new ArgumentOutOfRangeException ("offset");
460                         if (count < 0)
461                                 throw new ArgumentOutOfRangeException ("count");
462                         if (count > max_count)
463                                 count = max_count;
464
465                         if (buffering) {
466                                 // It does not matter whether we're in ApplyFilter or not
467                                 AppendBuffer (buffer, offset, count);
468                         } else if (filter == null || filtering) {
469                                 response.WriteHeaders (false);
470                                 HttpWorkerRequest wr = response.WorkerRequest;
471                                 // Direct write because not buffering
472                                 if (offset == 0) {
473                                         wr.SendResponseFromMemory (buffer, count);
474                                 } else {
475                                         UnsafeWrite (wr, buffer, offset, count);
476                                 }
477                                 wr.FlushResponse (false);
478                         } else {
479                                 // Write to the filter, which will call us back, and then Flush
480                                 filtering = true;
481                                 try {
482                                         filter.Write (buffer, offset, count);
483                                 } finally {
484                                         filtering = false;
485                                 }
486                                 Flush (response.WorkerRequest, false);
487                         }
488                 }
489
490                 unsafe void UnsafeWrite (HttpWorkerRequest wr, byte [] buffer, int offset, int count)
491                 {
492                         fixed (byte *ptr = buffer) {
493                                 wr.SendResponseFromMemory ((IntPtr) (ptr + offset), count);
494                         }
495                 }
496                 void AppendBuffer (byte [] buffer, int offset, int count)
497                 {
498                         if (!(cur_bucket is ByteBucket))
499                                 AppendBucket (new ByteBucket ());
500
501                         total += count;
502                         ((ByteBucket) cur_bucket).Write (buffer, offset, count);
503                 }
504
505                 void AppendBuffer (IntPtr ptr, int count)
506                 {
507                         if (!(cur_bucket is ByteBucket))
508                                 AppendBucket (new ByteBucket ());
509
510                         total += count;
511                         ((ByteBucket) cur_bucket).Write (ptr, count);
512                 }
513
514                 //
515                 // This should not flush/close or anything else, its called
516                 // just to free any memory we might have allocated (when we later
517                 // implement something with unmanaged memory).
518                 //
519                 internal void ReleaseResources (bool close_filter)
520                 {
521                         if (close_filter && filter != null) {
522                                 filter.Close ();
523                                 filter = null;
524                         }
525
526                         for (Bucket b = first_bucket; b != null; b = b.Next)
527                                 b.Dispose ();
528
529                         first_bucket = null;
530                         cur_bucket = null;
531                 }
532
533                 public void Clear ()
534                 {
535                         //
536                         // IMPORTANT: you must dispose *AFTER* using all the buckets Byte chunks might be
537                         // split across two buckets if there is a file between the data.
538                         //
539                         ReleaseResources (false);
540                         total = 0;
541                 }
542                 
543                 // Do not use directly. Use memcpy.
544                 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
545                         /*while (size >= 32) {
546                                 // using long is better than int and slower than double
547                                 // FIXME: enable this only on correct alignment or on platforms
548                                 // that can tolerate unaligned reads/writes of doubles
549                                 ((double*)dest) [0] = ((double*)src) [0];
550                                 ((double*)dest) [1] = ((double*)src) [1];
551                                 ((double*)dest) [2] = ((double*)src) [2];
552                                 ((double*)dest) [3] = ((double*)src) [3];
553                                 dest += 32;
554                                 src += 32;
555                                 size -= 32;
556                         }*/
557                         while (size >= 16) {
558                                 ((int*)dest) [0] = ((int*)src) [0];
559                                 ((int*)dest) [1] = ((int*)src) [1];
560                                 ((int*)dest) [2] = ((int*)src) [2];
561                                 ((int*)dest) [3] = ((int*)src) [3];
562                                 dest += 16;
563                                 src += 16;
564                                 size -= 16;
565                         }
566                         while (size >= 4) {
567                                 ((int*)dest) [0] = ((int*)src) [0];
568                                 dest += 4;
569                                 src += 4;
570                                 size -= 4;
571                         }
572                         while (size > 0) {
573                                 ((byte*)dest) [0] = ((byte*)src) [0];
574                                 dest += 1;
575                                 src += 1;
576                                 --size;
577                         }
578                 }
579
580                 // Do not use directly. Use memcpy.
581                 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
582                         while (size >= 8) {
583                                 ((short*)dest) [0] = ((short*)src) [0];
584                                 ((short*)dest) [1] = ((short*)src) [1];
585                                 ((short*)dest) [2] = ((short*)src) [2];
586                                 ((short*)dest) [3] = ((short*)src) [3];
587                                 dest += 8;
588                                 src += 8;
589                                 size -= 8;
590                         }
591                         while (size >= 2) {
592                                 ((short*)dest) [0] = ((short*)src) [0];
593                                 dest += 2;
594                                 src += 2;
595                                 size -= 2;
596                         }
597                         if (size > 0)
598                                 ((byte*)dest) [0] = ((byte*)src) [0];
599                 }
600
601                 // Do not use directly. Use memcpy.
602                 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
603                         while (size >= 8) {
604                                 ((byte*)dest) [0] = ((byte*)src) [0];
605                                 ((byte*)dest) [1] = ((byte*)src) [1];
606                                 ((byte*)dest) [2] = ((byte*)src) [2];
607                                 ((byte*)dest) [3] = ((byte*)src) [3];
608                                 ((byte*)dest) [4] = ((byte*)src) [4];
609                                 ((byte*)dest) [5] = ((byte*)src) [5];
610                                 ((byte*)dest) [6] = ((byte*)src) [6];
611                                 ((byte*)dest) [7] = ((byte*)src) [7];
612                                 dest += 8;
613                                 src += 8;
614                                 size -= 8;
615                         }
616                         while (size >= 2) {
617                                 ((byte*)dest) [0] = ((byte*)src) [0];
618                                 ((byte*)dest) [1] = ((byte*)src) [1];
619                                 dest += 2;
620                                 src += 2;
621                                 size -= 2;
622                         }
623                         if (size > 0)
624                                 ((byte*)dest) [0] = ((byte*)src) [0];
625                 }
626
627                 static unsafe void memcpy (byte *dest, byte *src, int size) {
628                         // FIXME: if pointers are not aligned, try to align them
629                         // so a faster routine can be used. Handle the case where
630                         // the pointers can't be reduced to have the same alignment
631                         // (just ignore the issue on x86?)
632                         if ((((int)dest | (int)src) & 3) != 0) {
633                                 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
634                                         dest [0] = src [0];
635                                         ++dest;
636                                         ++src;
637                                         --size;
638                                 }
639                                 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
640                                         ((short*)dest) [0] = ((short*)src) [0];
641                                         dest += 2;
642                                         src += 2;
643                                         size -= 2;
644                                 }
645                                 if ((((int)dest | (int)src) & 1) != 0) {
646                                         memcpy1 (dest, src, size);
647                                         return;
648                                 }
649                                 if ((((int)dest | (int)src) & 2) != 0) {
650                                         memcpy2 (dest, src, size);
651                                         return;
652                                 }
653                         }
654                         memcpy4 (dest, src, size);
655                 }
656
657                 public override bool CanRead {
658                         get {
659                                 return false;
660                         }
661                 }
662
663                 public override bool CanSeek {
664                         get {
665                                 return false;
666                         }
667                 }
668
669                 public override bool CanWrite {
670                         get {
671                                 return true;
672                         }
673                 }
674
675                 const string notsupported = "HttpResponseStream is a forward, write-only stream";
676
677                 public override long Length {
678                         get {
679                                 throw new NotSupportedException (notsupported);
680                         }
681                 }
682         
683                 public override long Position {
684                         get {
685                                 throw new NotSupportedException (notsupported);
686                         }
687                         set {
688                                 throw new NotSupportedException (notsupported);
689                         }
690                 }
691                 
692                 public override long Seek (long offset, SeekOrigin origin)
693                 {
694                         throw new NotSupportedException (notsupported);
695                 }
696
697                 public override void SetLength (long value) 
698                 {
699                         throw new NotSupportedException (notsupported);
700                 }
701         
702                 public override int Read (byte [] buffer, int offset, int count)
703                 {
704                         throw new NotSupportedException (notsupported);
705                 }
706         }
707 }