2 // System.Web.HttpResponseStream.cs
6 // Miguel de Icaza (miguel@novell.com)
7 // Ben Maurer (bmaurer@ximian.com)
10 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
35 using System.Globalization;
36 using System.Runtime.InteropServices;
38 namespace System.Web {
41 // HttpResponseStream implements the "OutputStream" from HttpResponse
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.
47 // You must call HttpResponse.Flush which does the actual header generation
48 // and actual data flushing
50 internal class HttpResponseStream : Stream {
53 HttpResponse response;
56 byte [] chunk_buffer = new byte [24];
58 public HttpResponseStream (HttpResponse response)
60 this.response = response;
63 internal bool HaveFilter {
64 get { return filter != null; }
67 public Stream Filter {
70 filter = new OutputFilterStream (this);
80 const int PreferredLength = 16 * 1024;
81 static readonly byte[] EmptyBuffer = new byte[0];
83 byte[] buffer = EmptyBuffer;
86 public BlockManager () {
90 get { return position; }
93 void EnsureCapacity (int capacity) {
94 if (buffer.Length >= capacity)
97 capacity += PreferredLength;
98 capacity = (capacity / PreferredLength) * PreferredLength;
99 byte[] temp = new byte[capacity];
100 Array.Copy(buffer, 0, temp, 0, buffer.Length);
104 public void Write (byte [] buffer, int offset, int count) {
108 EnsureCapacity (position + count);
109 Array.Copy(buffer, offset, this.buffer, position, count);
113 public void Send (HttpWorkerRequest wr, int start, int end) {
114 int length = end - start;
118 if (length > buffer.Length - start)
119 length = buffer.Length - start;
122 byte[] temp = new byte[length];
123 Array.Copy(buffer, start, temp, 0, length);
126 wr.SendResponseFromMemory(buffer, length);
129 public void Send (Stream stream, int start, int end) {
130 int length = end - start;
134 if (length > buffer.Length - start)
135 length = buffer.Length - start;
137 stream.Write(buffer, start, length);
140 public void Dispose () {
146 unsafe sealed class BlockManager {
147 const int PreferredLength = 128 * 1024;
152 public BlockManager ()
156 public int Position {
157 get { return position; }
160 void EnsureCapacity (int capacity)
162 if (block_size >= capacity)
165 capacity += PreferredLength;
166 capacity = (capacity / PreferredLength) * PreferredLength;
169 ? (byte *) Marshal.AllocHGlobal (capacity)
170 : (byte *) Marshal.ReAllocHGlobal ((IntPtr) data, (IntPtr) capacity);
171 block_size = capacity;
174 public void Write (byte [] buffer, int offset, int count)
179 EnsureCapacity (position + count);
180 Marshal.Copy (buffer, offset, (IntPtr) (data + position), count);
184 public void Write (IntPtr ptr, int count)
189 EnsureCapacity (position + count);
190 byte *src = (byte *) ptr.ToPointer ();
191 memcpy (data + position, src, count);
195 public void Send (HttpWorkerRequest wr, int start, int end)
197 if (end - start <= 0)
200 wr.SendResponseFromMemory ((IntPtr) (data + start), end - start);
203 public void Send (Stream stream, int start, int end)
205 int len = end - start;
209 byte [] buffer = new byte [Math.Min (len, 32 * 1024)];
210 int size = buffer.Length;
212 Marshal.Copy ((IntPtr) (data + start), buffer, 0, size);
213 stream.Write (buffer, 0, size);
216 if (len > 0 && len < size)
221 public void Dispose ()
223 if ((IntPtr) data != IntPtr.Zero) {
224 Marshal.FreeHGlobal ((IntPtr) data);
225 data = (byte *) IntPtr.Zero;
231 abstract class Bucket {
234 public virtual void Dispose ()
238 public abstract void Send (HttpWorkerRequest wr);
239 public abstract void Send (Stream stream);
240 public abstract int Length { get; }
246 class ByteBucket : Bucket {
249 public BlockManager blocks;
250 public bool Expandable = true;
252 public ByteBucket () : this (null)
256 public ByteBucket (BlockManager blocks)
259 blocks = new BlockManager ();
261 this.blocks = blocks;
262 start = blocks.Position;
265 public override int Length {
266 get { return length; }
269 public unsafe int Write (byte [] buf, int offset, int count)
271 if (Expandable == false)
272 throw new Exception ("This should not happen.");
274 fixed (byte *p = &buf[0]) {
275 IntPtr p2 = new IntPtr (p + offset);
276 blocks.Write (p2, count);
283 public int Write (IntPtr ptr, int count)
285 if (Expandable == false)
286 throw new Exception ("This should not happen.");
288 blocks.Write (ptr, count);
293 public override void Dispose ()
298 public override void Send (HttpWorkerRequest wr)
303 blocks.Send (wr, start, length);
306 public override void Send (Stream stream)
311 blocks.Send (stream, start, length);
315 class BufferedFileBucket : Bucket {
320 public BufferedFileBucket (string f, long off, long len)
327 public override int Length {
328 get { return (int) length; }
331 public override void Send (HttpWorkerRequest wr)
333 wr.SendResponseFromFile (file, offset, length);
336 public override void Send (Stream stream)
338 using (FileStream fs = File.OpenRead (file)) {
339 byte [] buffer = new byte [Math.Min (fs.Length, 32*1024)];
341 long remain = fs.Length;
343 while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
345 stream.Write (buffer, 0, n);
350 public override string ToString ()
352 return "file " + file + " " + length.ToString () + " bytes from position " + offset.ToString ();
356 void AppendBucket (Bucket b)
358 if (first_bucket == null) {
359 cur_bucket = first_bucket = b;
368 // Nothing happens here, broken by requirement.
369 // See note at the start
371 public override void Flush ()
375 void SendChunkSize (long l, bool last)
382 string s = l.ToString ("x");
383 for (; i < s.Length; i++)
384 chunk_buffer [i] = (byte) s [i];
387 chunk_buffer [i++] = 13;
388 chunk_buffer [i++] = 10;
390 chunk_buffer [i++] = 13;
391 chunk_buffer [i++] = 10;
394 response.WorkerRequest.SendResponseFromMemory (chunk_buffer, i);
397 internal void Flush (HttpWorkerRequest wr, bool final_flush)
399 if (total == 0 && !final_flush)
402 if (response.use_chunked)
403 SendChunkSize (total, false);
405 for (Bucket b = first_bucket; b != null; b = b.Next) {
409 if (response.use_chunked) {
410 SendChunkSize (-1, false);
412 SendChunkSize (0, true);
415 wr.FlushResponse (final_flush);
420 internal int GetTotalLength ()
423 for (Bucket b = first_bucket; b != null; b = b.Next)
429 internal MemoryStream GetData ()
431 MemoryStream stream = new MemoryStream ();
432 for (Bucket b = first_bucket; b != null; b = b.Next)
437 public void WriteFile (string f, long offset, long length)
442 ByteBucket bb = cur_bucket as ByteBucket;
445 bb.Expandable = false;
446 bb = new ByteBucket (bb.blocks);
451 AppendBucket (new BufferedFileBucket (f, offset, length));
454 // Flush () is called from HttpResponse if needed (WriteFile/TransmitFile)
458 internal void ApplyFilter (bool close)
464 Bucket one = first_bucket;
465 first_bucket = null; // This will recreate new buckets for the filtered content
468 for (Bucket b = one; b != null; b = b.Next)
471 for (Bucket b = one; b != null; b = b.Next)
484 public void WritePtr (IntPtr ptr, int length)
489 bool buffering = response.BufferOutput;
492 // It does not matter whether we're in ApplyFilter or not
493 AppendBuffer (ptr, length);
494 } else if (filter == null || filtering) {
495 response.WriteHeaders (false);
496 HttpWorkerRequest wr = response.WorkerRequest;
497 // Direct write because not buffering
498 wr.SendResponseFromMemory (ptr, length);
499 wr.FlushResponse (false);
501 // Write to the filter, which will call us back, and then Flush
504 byte [] bytes = new byte [length];
505 Marshal.Copy (ptr, bytes, 0, length);
506 filter.Write (bytes, 0, length);
511 Flush (response.WorkerRequest, false);
516 public override void Write (byte [] buffer, int offset, int count)
518 bool buffering = response.BufferOutput;
522 throw new ArgumentNullException ("buffer");
525 int max_count = buffer.Length - offset;
527 if (offset < 0 || max_count <= 0)
531 throw new ArgumentOutOfRangeException ("offset");
533 throw new ArgumentOutOfRangeException ("count");
534 if (count > max_count)
542 // It does not matter whether we're in ApplyFilter or not
543 AppendBuffer (buffer, offset, count);
544 } else if (filter == null || filtering) {
545 response.WriteHeaders (false);
546 HttpWorkerRequest wr = response.WorkerRequest;
547 // Direct write because not buffering
549 wr.SendResponseFromMemory (buffer, count);
551 UnsafeWrite (wr, buffer, offset, count);
553 wr.FlushResponse (false);
555 // Write to the filter, which will call us back, and then Flush
558 filter.Write (buffer, offset, count);
562 Flush (response.WorkerRequest, false);
567 void UnsafeWrite (HttpWorkerRequest wr, byte [] buffer, int offset, int count)
572 byte[] copy = new byte[count];
573 Array.Copy(buffer, offset, copy, 0, count);
574 wr.SendResponseFromMemory (copy, count);
577 unsafe void UnsafeWrite (HttpWorkerRequest wr, byte [] buffer, int offset, int count)
579 fixed (byte *ptr = buffer) {
580 wr.SendResponseFromMemory ((IntPtr) (ptr + offset), count);
584 void AppendBuffer (byte [] buffer, int offset, int count)
586 if (!(cur_bucket is ByteBucket))
587 AppendBucket (new ByteBucket ());
590 ((ByteBucket) cur_bucket).Write (buffer, offset, count);
593 void AppendBuffer (IntPtr ptr, int count)
595 if (!(cur_bucket is ByteBucket))
596 AppendBucket (new ByteBucket ());
599 ((ByteBucket) cur_bucket).Write (ptr, count);
603 // This should not flush/close or anything else, its called
604 // just to free any memory we might have allocated (when we later
605 // implement something with unmanaged memory).
607 internal void ReleaseResources (bool close_filter)
609 if (close_filter && filter != null) {
614 for (Bucket b = first_bucket; b != null; b = b.Next)
624 // IMPORTANT: you must dispose *AFTER* using all the buckets Byte chunks might be
625 // split across two buckets if there is a file between the data.
627 ReleaseResources (false);
631 // Do not use directly. Use memcpy.
632 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
633 /*while (size >= 32) {
634 // using long is better than int and slower than double
635 // FIXME: enable this only on correct alignment or on platforms
636 // that can tolerate unaligned reads/writes of doubles
637 ((double*)dest) [0] = ((double*)src) [0];
638 ((double*)dest) [1] = ((double*)src) [1];
639 ((double*)dest) [2] = ((double*)src) [2];
640 ((double*)dest) [3] = ((double*)src) [3];
646 ((int*)dest) [0] = ((int*)src) [0];
647 ((int*)dest) [1] = ((int*)src) [1];
648 ((int*)dest) [2] = ((int*)src) [2];
649 ((int*)dest) [3] = ((int*)src) [3];
655 ((int*)dest) [0] = ((int*)src) [0];
661 ((byte*)dest) [0] = ((byte*)src) [0];
668 // Do not use directly. Use memcpy.
669 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
671 ((short*)dest) [0] = ((short*)src) [0];
672 ((short*)dest) [1] = ((short*)src) [1];
673 ((short*)dest) [2] = ((short*)src) [2];
674 ((short*)dest) [3] = ((short*)src) [3];
680 ((short*)dest) [0] = ((short*)src) [0];
686 ((byte*)dest) [0] = ((byte*)src) [0];
689 // Do not use directly. Use memcpy.
690 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
692 ((byte*)dest) [0] = ((byte*)src) [0];
693 ((byte*)dest) [1] = ((byte*)src) [1];
694 ((byte*)dest) [2] = ((byte*)src) [2];
695 ((byte*)dest) [3] = ((byte*)src) [3];
696 ((byte*)dest) [4] = ((byte*)src) [4];
697 ((byte*)dest) [5] = ((byte*)src) [5];
698 ((byte*)dest) [6] = ((byte*)src) [6];
699 ((byte*)dest) [7] = ((byte*)src) [7];
705 ((byte*)dest) [0] = ((byte*)src) [0];
706 ((byte*)dest) [1] = ((byte*)src) [1];
712 ((byte*)dest) [0] = ((byte*)src) [0];
715 static unsafe void memcpy (byte *dest, byte *src, int size) {
716 // FIXME: if pointers are not aligned, try to align them
717 // so a faster routine can be used. Handle the case where
718 // the pointers can't be reduced to have the same alignment
719 // (just ignore the issue on x86?)
720 if ((((int)dest | (int)src) & 3) != 0) {
721 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
727 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
728 ((short*)dest) [0] = ((short*)src) [0];
733 if ((((int)dest | (int)src) & 1) != 0) {
734 memcpy1 (dest, src, size);
737 if ((((int)dest | (int)src) & 2) != 0) {
738 memcpy2 (dest, src, size);
742 memcpy4 (dest, src, size);
745 public override bool CanRead {
751 public override bool CanSeek {
757 public override bool CanWrite {
763 const string notsupported = "HttpResponseStream is a forward, write-only stream";
765 public override long Length {
767 throw new NotSupportedException (notsupported);
771 public override long Position {
773 throw new NotSupportedException (notsupported);
776 throw new NotSupportedException (notsupported);
780 public override long Seek (long offset, SeekOrigin origin)
782 throw new NotSupportedException (notsupported);
785 public override void SetLength (long value)
787 throw new NotSupportedException (notsupported);
790 public override int Read (byte [] buffer, int offset, int count)
792 throw new NotSupportedException (notsupported);