[System.Net] Add support for .pac proxy config scripts on mac
[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 #if TARGET_JVM
79
80                 class BlockManager {
81                         const int PreferredLength = 16 * 1024;
82                         static readonly byte[] EmptyBuffer = new byte[0];
83
84                         byte[] buffer = EmptyBuffer;
85                         int position;
86
87                         public BlockManager () {
88                         }
89
90                         public int Position {
91                                 get { return position; }
92                         }
93
94                         void EnsureCapacity (int capacity) {
95                                 if (buffer.Length >= capacity)
96                                         return;
97
98                                 capacity += PreferredLength;
99                                 capacity = (capacity / PreferredLength) * PreferredLength;
100                                 byte[] temp = new byte[capacity];
101                                 Array.Copy(buffer, 0, temp, 0, buffer.Length);
102                                 buffer = temp;
103                         }
104
105                         public void Write (byte [] buffer, int offset, int count) {
106                                 if (count == 0)
107                                         return;
108
109                                 EnsureCapacity (position + count);
110                                 Array.Copy(buffer, offset, this.buffer, position, count);
111                                 position += count;
112                         }
113
114                         public void Send (HttpWorkerRequest wr, int start, int end) {
115                                 int length = end - start;
116                                 if (length <= 0)
117                                         return;
118
119                                 if (length > buffer.Length - start)
120                                         length = buffer.Length - start;
121
122                                 if (start > 0) {
123                                         byte[] temp = new byte[length];
124                                         Array.Copy(buffer, start, temp, 0, length);
125                                         buffer = temp;
126                                 }
127                                 wr.SendResponseFromMemory(buffer, length);
128                         }
129
130                         public void Send (Stream stream, int start, int end) {
131                                 int length = end - start;
132                                 if (length <= 0)
133                                         return;
134
135                                 if (length > buffer.Length - start)
136                                         length = buffer.Length - start;
137
138                                 stream.Write(buffer, start, length);
139                         }
140
141                         public void Dispose () {
142                                 buffer = null;
143                         }
144                 }
145
146 #else // TARGET_JVM
147                 unsafe sealed class BlockManager {
148                         const int PreferredLength = 128 * 1024;
149                         byte *data;
150                         int position;
151                         int block_size;
152
153                         public BlockManager ()
154                         {
155                         }
156
157                         public int Position {
158                                 get { return position; }
159                         }
160
161                         void EnsureCapacity (int capacity)
162                         {
163                                 if (block_size >= capacity)
164                                         return;
165
166                                 capacity += PreferredLength;
167                                 capacity = (capacity / PreferredLength) * PreferredLength;
168
169                                 data = data == null
170                                         ? (byte *) Marshal.AllocHGlobal (capacity)
171                                         : (byte *) Marshal.ReAllocHGlobal ((IntPtr) data, (IntPtr) capacity);
172                                 block_size = capacity;
173                         }
174
175                         public void Write (byte [] buffer, int offset, int count)
176                         {
177                                 if (count == 0)
178                                         return;
179                                 
180                                 EnsureCapacity (position + count);
181                                 Marshal.Copy (buffer, offset, (IntPtr) (data + position), count);
182                                 position += count;
183                         }
184
185                         public void Write (IntPtr ptr, int count)
186                         {
187                                 if (count == 0)
188                                         return;
189                                 
190                                 EnsureCapacity (position + count);
191                                 byte *src = (byte *) ptr.ToPointer ();
192                                 if (count < 32) {
193                                         byte *dest = (data + position);
194                                         for (int i = 0; i < count; i++)
195                                                 *dest++ = *src++;
196                                 } else {
197                                         memcpy (data + position, src, count);
198                                 }
199                                 position += count;
200                         }
201
202                         public void Send (HttpWorkerRequest wr, int start, int end)
203                         {
204                                 if (end - start <= 0)
205                                         return;
206
207                                 wr.SendResponseFromMemory ((IntPtr) (data + start), end - start);
208                         }
209
210                         public void Send (Stream stream, int start, int end)
211                         {
212                                 int len = end - start;
213                                 if (len <= 0)
214                                         return;
215
216                                 byte [] buffer = new byte [Math.Min (len, 32 * 1024)];
217                                 int size = buffer.Length;
218                                 while (len > 0) {
219                                         Marshal.Copy ((IntPtr) (data + start), buffer, 0, size);
220                                         stream.Write (buffer, 0, size);
221                                         start += size;
222                                         len -= size;
223                                         if (len > 0 && len < size)
224                                                 size = len;
225                                 }
226                         }
227                         
228                         public void Dispose ()
229                         {
230                                 if ((IntPtr) data != IntPtr.Zero) {
231                                         Marshal.FreeHGlobal ((IntPtr) data);
232                                         data = (byte *) IntPtr.Zero;
233                                 }
234                         }
235                 }
236
237 #endif
238                 abstract class Bucket {
239                         public Bucket Next;
240
241                         public virtual void Dispose ()
242                         {
243                         }
244
245                         public abstract void Send (HttpWorkerRequest wr);
246                         public abstract void Send (Stream stream);
247                         public abstract int Length { get; }
248                 }
249
250 #if !TARGET_JVM
251                 unsafe
252 #endif
253                 class ByteBucket : Bucket {
254                         int start;
255                         int length;
256                         public BlockManager blocks;
257                         public bool Expandable = true;
258
259                         public ByteBucket () : this (null)
260                         {
261                         }
262
263                         public ByteBucket (BlockManager blocks)
264                         {
265                                 if (blocks == null)
266                                         blocks = new BlockManager ();
267
268                                 this.blocks = blocks;
269                                 start = blocks.Position;
270                         }
271
272                         public override int Length {
273                                 get { return length; }
274                         }
275
276                         public unsafe int Write (byte [] buf, int offset, int count)
277                         {
278                                 if (Expandable == false)
279                                         throw new Exception ("This should not happen.");
280
281                                 fixed (byte *p = &buf[0]) {
282                                         IntPtr p2 = new IntPtr (p + offset);
283                                         blocks.Write (p2, count);
284                                 }
285
286                                 length += count;
287                                 return count;
288                         }
289
290                         public int Write (IntPtr ptr, int count)
291                         {
292                                 if (Expandable == false)
293                                         throw new Exception ("This should not happen.");
294
295                                 blocks.Write (ptr, count);
296                                 length += count;
297                                 return count;
298                         }
299
300                         public override void Dispose ()
301                         {
302                                 blocks.Dispose ();
303                         }
304
305                         public override void Send (HttpWorkerRequest wr)
306                         {
307                                 if (length == 0)
308                                         return;
309
310                                 blocks.Send (wr, start, length);
311                         }
312
313                         public override void Send (Stream stream)
314                         {
315                                 if (length == 0)
316                                         return;
317
318                                 blocks.Send (stream, start, length);
319                         }
320                 }
321
322                 class BufferedFileBucket : Bucket {
323                         string file;
324                         long offset;
325                         long length;
326         
327                         public BufferedFileBucket (string f, long off, long len)
328                         {
329                                 file = f;
330                                 offset = off;
331                                 length = len;
332                         }
333
334                         public override int Length {
335                                 get { return (int) length; }
336                         }
337
338                         public override void Send (HttpWorkerRequest wr)
339                         {
340                                 wr.SendResponseFromFile (file, offset, length);
341                         }
342
343                         public override void Send (Stream stream)
344                         {
345                                 using (FileStream fs = File.OpenRead (file)) {
346                                         byte [] buffer = new byte [Math.Min (fs.Length, 32*1024)];
347
348                                         long remain = fs.Length;
349                                         int n;
350                                         while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
351                                                 remain -= n;
352                                                 stream.Write (buffer, 0, n);
353                                         }
354                                 }
355                         }
356
357                         public override string ToString ()
358                         {
359                                 return "file " + file + " " + length.ToString () + " bytes from position " + offset.ToString ();
360                         }       
361                 }
362         
363                 void AppendBucket (Bucket b)
364                 {
365                         if (first_bucket == null) {
366                                 cur_bucket = first_bucket = b;
367                                 return;
368                         }
369         
370                         cur_bucket.Next = b;
371                         cur_bucket = b;
372                 }
373         
374                 //
375                 // Nothing happens here, broken by requirement.
376                 // See note at the start
377                 //
378                 public override void Flush () 
379                 {
380                 }
381
382                 void SendChunkSize (long l, bool last)
383                 {
384                         if (l == 0 && !last)
385                                 return;
386
387                         int i = 0;
388                         if (l >= 0) {
389                                 string s = l.ToString ("x");
390                                 for (; i < s.Length; i++)
391                                         chunk_buffer [i] = (byte) s [i];
392                         }
393
394                         chunk_buffer [i++] = 13;
395                         chunk_buffer [i++] = 10;
396                         if (last) {
397                                 chunk_buffer [i++] = 13;
398                                 chunk_buffer [i++] = 10;
399                         }
400
401                         response.WorkerRequest.SendResponseFromMemory (chunk_buffer, i);
402                 }
403
404                 internal void Flush (HttpWorkerRequest wr, bool final_flush)
405                 {
406                         if (total == 0 && !final_flush)
407                                 return;
408
409                         if (response.use_chunked) 
410                                 SendChunkSize (total, false);
411
412                         for (Bucket b = first_bucket; b != null; b = b.Next) {
413                                 b.Send (wr);
414                         }
415
416                         if (response.use_chunked) {
417                                 SendChunkSize (-1, false);
418                                 if (final_flush)
419                                         SendChunkSize (0, true);
420                         }
421
422                         wr.FlushResponse (final_flush);
423
424                         Clear ();
425                 }
426
427                 internal int GetTotalLength ()
428                 {
429                         int size = 0;
430                         for (Bucket b = first_bucket; b != null; b = b.Next)
431                                 size += b.Length;
432
433                         return size;
434                 }
435
436                 internal MemoryStream GetData ()
437                 {
438                         MemoryStream stream = new MemoryStream ();
439                         for (Bucket b = first_bucket; b != null; b = b.Next)
440                                 b.Send (stream);
441                         return stream;
442                 }
443
444                 public void WriteFile (string f, long offset, long length)
445                 {
446                         if (length == 0)
447                                 return;
448
449                         ByteBucket bb = cur_bucket as ByteBucket;
450
451                         if (bb != null) {
452                                 bb.Expandable = false;
453                                 bb = new ByteBucket (bb.blocks);
454                         }
455
456                         total += length;
457                         
458                         AppendBucket (new BufferedFileBucket (f, offset, length));
459                         if (bb != null)
460                                 AppendBucket (bb);
461                         // Flush () is called from HttpResponse if needed (WriteFile/TransmitFile)
462                 }
463
464                 bool filtering;
465                 internal void ApplyFilter (bool close)
466                 {
467                         if (filter == null)
468                                 return;
469
470                         filtering = true;
471                         Bucket one = first_bucket;
472                         first_bucket = null; // This will recreate new buckets for the filtered content
473                         cur_bucket = null;
474                         total = 0;
475                         for (Bucket b = one; b != null; b = b.Next)
476                                 b.Send (filter);
477
478                         for (Bucket b = one; b != null; b = b.Next)
479                                 b.Dispose ();
480
481                         if (close) {
482                                 filter.Flush ();
483                                 filter.Close ();
484                                 filter = null;
485                         } else {
486                                 filter.Flush ();
487                         }
488                         filtering = false;
489                 }
490
491                 public void WritePtr (IntPtr ptr, int length)
492                 {
493                         if (length == 0)
494                                 return;
495
496                         bool buffering = response.BufferOutput;
497
498                         if (buffering) {
499                                 // It does not matter whether we're in ApplyFilter or not
500                                 AppendBuffer (ptr, length);
501                         } else if (filter == null || filtering) {
502                                 response.WriteHeaders (false);
503                                 HttpWorkerRequest wr = response.WorkerRequest;
504                                 // Direct write because not buffering
505                                 wr.SendResponseFromMemory (ptr, length);
506                                 wr.FlushResponse (false);
507                         } else {
508                                 // Write to the filter, which will call us back, and then Flush
509                                 filtering = true;
510                                 try {
511                                         byte [] bytes = new byte [length];
512                                         Marshal.Copy (ptr, bytes, 0, length);
513                                         filter.Write (bytes, 0, length);
514                                         bytes = null;
515                                 } finally {
516                                         filtering = false;
517                                 }
518                                 Flush (response.WorkerRequest, false);
519                         }
520
521                 }
522
523                 public override void Write (byte [] buffer, int offset, int count)
524                 {
525                         bool buffering = response.BufferOutput;
526                         if (buffer == null)
527                                 throw new ArgumentNullException ("buffer");
528
529                         int max_count = buffer.Length - offset;
530                         if (offset < 0 || max_count <= 0)
531                                 throw new ArgumentOutOfRangeException ("offset");
532                         if (count < 0)
533                                 throw new ArgumentOutOfRangeException ("count");
534                         if (count > max_count)
535                                 count = max_count;
536
537                         if (buffering) {
538                                 // It does not matter whether we're in ApplyFilter or not
539                                 AppendBuffer (buffer, offset, count);
540                         } else if (filter == null || filtering) {
541                                 response.WriteHeaders (false);
542                                 HttpWorkerRequest wr = response.WorkerRequest;
543                                 // Direct write because not buffering
544                                 if (offset == 0) {
545                                         wr.SendResponseFromMemory (buffer, count);
546                                 } else {
547                                         UnsafeWrite (wr, buffer, offset, count);
548                                 }
549                                 wr.FlushResponse (false);
550                         } else {
551                                 // Write to the filter, which will call us back, and then Flush
552                                 filtering = true;
553                                 try {
554                                         filter.Write (buffer, offset, count);
555                                 } finally {
556                                         filtering = false;
557                                 }
558                                 Flush (response.WorkerRequest, false);
559                         }
560                 }
561
562 #if TARGET_JVM
563                 void UnsafeWrite (HttpWorkerRequest wr, byte [] buffer, int offset, int count)
564                 {
565                         if (count <= 0)
566                                 return;
567
568                         byte[] copy = new byte[count];
569                         Array.Copy(buffer, offset, copy, 0, count);
570                         wr.SendResponseFromMemory (copy, count);
571                 }
572 #else
573                 unsafe void UnsafeWrite (HttpWorkerRequest wr, byte [] buffer, int offset, int count)
574                 {
575                         fixed (byte *ptr = buffer) {
576                                 wr.SendResponseFromMemory ((IntPtr) (ptr + offset), count);
577                         }
578                 }
579 #endif
580                 void AppendBuffer (byte [] buffer, int offset, int count)
581                 {
582                         if (!(cur_bucket is ByteBucket))
583                                 AppendBucket (new ByteBucket ());
584
585                         total += count;
586                         ((ByteBucket) cur_bucket).Write (buffer, offset, count);
587                 }
588
589                 void AppendBuffer (IntPtr ptr, int count)
590                 {
591                         if (!(cur_bucket is ByteBucket))
592                                 AppendBucket (new ByteBucket ());
593
594                         total += count;
595                         ((ByteBucket) cur_bucket).Write (ptr, count);
596                 }
597
598                 //
599                 // This should not flush/close or anything else, its called
600                 // just to free any memory we might have allocated (when we later
601                 // implement something with unmanaged memory).
602                 //
603                 internal void ReleaseResources (bool close_filter)
604                 {
605                         if (close_filter && filter != null) {
606                                 filter.Close ();
607                                 filter = null;
608                         }
609
610                         for (Bucket b = first_bucket; b != null; b = b.Next)
611                                 b.Dispose ();
612
613                         first_bucket = null;
614                         cur_bucket = null;
615                 }
616
617                 public void Clear ()
618                 {
619                         //
620                         // IMPORTANT: you must dispose *AFTER* using all the buckets Byte chunks might be
621                         // split across two buckets if there is a file between the data.
622                         //
623                         ReleaseResources (false);
624                         total = 0;
625                 }
626                 
627                 // Do not use directly. Use memcpy.
628                 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
629                         /*while (size >= 32) {
630                                 // using long is better than int and slower than double
631                                 // FIXME: enable this only on correct alignment or on platforms
632                                 // that can tolerate unaligned reads/writes of doubles
633                                 ((double*)dest) [0] = ((double*)src) [0];
634                                 ((double*)dest) [1] = ((double*)src) [1];
635                                 ((double*)dest) [2] = ((double*)src) [2];
636                                 ((double*)dest) [3] = ((double*)src) [3];
637                                 dest += 32;
638                                 src += 32;
639                                 size -= 32;
640                         }*/
641                         while (size >= 16) {
642                                 ((int*)dest) [0] = ((int*)src) [0];
643                                 ((int*)dest) [1] = ((int*)src) [1];
644                                 ((int*)dest) [2] = ((int*)src) [2];
645                                 ((int*)dest) [3] = ((int*)src) [3];
646                                 dest += 16;
647                                 src += 16;
648                                 size -= 16;
649                         }
650                         while (size >= 4) {
651                                 ((int*)dest) [0] = ((int*)src) [0];
652                                 dest += 4;
653                                 src += 4;
654                                 size -= 4;
655                         }
656                         while (size > 0) {
657                                 ((byte*)dest) [0] = ((byte*)src) [0];
658                                 dest += 1;
659                                 src += 1;
660                                 --size;
661                         }
662                 }
663
664                 // Do not use directly. Use memcpy.
665                 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
666                         while (size >= 8) {
667                                 ((short*)dest) [0] = ((short*)src) [0];
668                                 ((short*)dest) [1] = ((short*)src) [1];
669                                 ((short*)dest) [2] = ((short*)src) [2];
670                                 ((short*)dest) [3] = ((short*)src) [3];
671                                 dest += 8;
672                                 src += 8;
673                                 size -= 8;
674                         }
675                         while (size >= 2) {
676                                 ((short*)dest) [0] = ((short*)src) [0];
677                                 dest += 2;
678                                 src += 2;
679                                 size -= 2;
680                         }
681                         if (size > 0)
682                                 ((byte*)dest) [0] = ((byte*)src) [0];
683                 }
684
685                 // Do not use directly. Use memcpy.
686                 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
687                         while (size >= 8) {
688                                 ((byte*)dest) [0] = ((byte*)src) [0];
689                                 ((byte*)dest) [1] = ((byte*)src) [1];
690                                 ((byte*)dest) [2] = ((byte*)src) [2];
691                                 ((byte*)dest) [3] = ((byte*)src) [3];
692                                 ((byte*)dest) [4] = ((byte*)src) [4];
693                                 ((byte*)dest) [5] = ((byte*)src) [5];
694                                 ((byte*)dest) [6] = ((byte*)src) [6];
695                                 ((byte*)dest) [7] = ((byte*)src) [7];
696                                 dest += 8;
697                                 src += 8;
698                                 size -= 8;
699                         }
700                         while (size >= 2) {
701                                 ((byte*)dest) [0] = ((byte*)src) [0];
702                                 ((byte*)dest) [1] = ((byte*)src) [1];
703                                 dest += 2;
704                                 src += 2;
705                                 size -= 2;
706                         }
707                         if (size > 0)
708                                 ((byte*)dest) [0] = ((byte*)src) [0];
709                 }
710
711                 static unsafe void memcpy (byte *dest, byte *src, int size) {
712                         // FIXME: if pointers are not aligned, try to align them
713                         // so a faster routine can be used. Handle the case where
714                         // the pointers can't be reduced to have the same alignment
715                         // (just ignore the issue on x86?)
716                         if ((((int)dest | (int)src) & 3) != 0) {
717                                 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
718                                         dest [0] = src [0];
719                                         ++dest;
720                                         ++src;
721                                         --size;
722                                 }
723                                 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
724                                         ((short*)dest) [0] = ((short*)src) [0];
725                                         dest += 2;
726                                         src += 2;
727                                         size -= 2;
728                                 }
729                                 if ((((int)dest | (int)src) & 1) != 0) {
730                                         memcpy1 (dest, src, size);
731                                         return;
732                                 }
733                                 if ((((int)dest | (int)src) & 2) != 0) {
734                                         memcpy2 (dest, src, size);
735                                         return;
736                                 }
737                         }
738                         memcpy4 (dest, src, size);
739                 }
740
741                 public override bool CanRead {
742                         get {
743                                 return false;
744                         }
745                 }
746
747                 public override bool CanSeek {
748                         get {
749                                 return false;
750                         }
751                 }
752
753                 public override bool CanWrite {
754                         get {
755                                 return true;
756                         }
757                 }
758
759                 const string notsupported = "HttpResponseStream is a forward, write-only stream";
760
761                 public override long Length {
762                         get {
763                                 throw new NotSupportedException (notsupported);
764                         }
765                 }
766         
767                 public override long Position {
768                         get {
769                                 throw new NotSupportedException (notsupported);
770                         }
771                         set {
772                                 throw new NotSupportedException (notsupported);
773                         }
774                 }
775                 
776                 public override long Seek (long offset, SeekOrigin origin)
777                 {
778                         throw new NotSupportedException (notsupported);
779                 }
780
781                 public override void SetLength (long value) 
782                 {
783                         throw new NotSupportedException (notsupported);
784                 }
785         
786                 public override int Read (byte [] buffer, int offset, int count)
787                 {
788                         throw new NotSupportedException (notsupported);
789                 }
790         }
791 }