2 // System.Web.HttpResponseStream.cs
6 // Miguel de Icaza (miguel@novell.com)
7 // Ben Maurer (bmaurer@ximian.com)
10 // Copyright (C) 2005-2009 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;
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 class HttpResponseStream : Stream
54 HttpResponse response;
57 byte [] chunk_buffer = new byte [24];
59 public HttpResponseStream (HttpResponse response)
61 this.response = response;
64 internal bool HaveFilter {
65 get { return filter != null; }
68 public Stream Filter {
71 filter = new OutputFilterStream (this);
81 const int PreferredLength = 16 * 1024;
82 static readonly byte[] EmptyBuffer = new byte[0];
84 byte[] buffer = EmptyBuffer;
87 public BlockManager () {
91 get { return position; }
94 void EnsureCapacity (int capacity) {
95 if (buffer.Length >= capacity)
98 capacity += PreferredLength;
99 capacity = (capacity / PreferredLength) * PreferredLength;
100 byte[] temp = new byte[capacity];
101 Array.Copy(buffer, 0, temp, 0, buffer.Length);
105 public void Write (byte [] buffer, int offset, int count) {
109 EnsureCapacity (position + count);
110 Array.Copy(buffer, offset, this.buffer, position, count);
114 public void Send (HttpWorkerRequest wr, int start, int end) {
115 int length = end - start;
119 if (length > buffer.Length - start)
120 length = buffer.Length - start;
123 byte[] temp = new byte[length];
124 Array.Copy(buffer, start, temp, 0, length);
127 wr.SendResponseFromMemory(buffer, length);
130 public void Send (Stream stream, int start, int end) {
131 int length = end - start;
135 if (length > buffer.Length - start)
136 length = buffer.Length - start;
138 stream.Write(buffer, start, length);
141 public void Dispose () {
147 unsafe sealed class BlockManager {
148 const int PreferredLength = 128 * 1024;
153 public BlockManager ()
157 public int Position {
158 get { return position; }
161 void EnsureCapacity (int capacity)
163 if (block_size >= capacity)
166 capacity += PreferredLength;
167 capacity = (capacity / PreferredLength) * PreferredLength;
170 ? (byte *) Marshal.AllocHGlobal (capacity)
171 : (byte *) Marshal.ReAllocHGlobal ((IntPtr) data, (IntPtr) capacity);
172 block_size = capacity;
175 public void Write (byte [] buffer, int offset, int count)
180 EnsureCapacity (position + count);
181 Marshal.Copy (buffer, offset, (IntPtr) (data + position), count);
185 public void Write (IntPtr ptr, int count)
190 EnsureCapacity (position + count);
191 byte *src = (byte *) ptr.ToPointer ();
193 byte *dest = (data + position);
194 for (int i = 0; i < count; i++)
197 memcpy (data + position, src, count);
202 public void Send (HttpWorkerRequest wr, int start, int end)
204 if (end - start <= 0)
207 wr.SendResponseFromMemory ((IntPtr) (data + start), end - start);
210 public void Send (Stream stream, int start, int end)
212 int len = end - start;
216 byte [] buffer = new byte [Math.Min (len, 32 * 1024)];
217 int size = buffer.Length;
219 Marshal.Copy ((IntPtr) (data + start), buffer, 0, size);
220 stream.Write (buffer, 0, size);
223 if (len > 0 && len < size)
228 public void Dispose ()
230 if ((IntPtr) data != IntPtr.Zero) {
231 Marshal.FreeHGlobal ((IntPtr) data);
232 data = (byte *) IntPtr.Zero;
238 abstract class Bucket {
241 public virtual void Dispose ()
245 public abstract void Send (HttpWorkerRequest wr);
246 public abstract void Send (Stream stream);
247 public abstract int Length { get; }
253 class ByteBucket : Bucket {
256 public BlockManager blocks;
257 public bool Expandable = true;
259 public ByteBucket () : this (null)
263 public ByteBucket (BlockManager blocks)
266 blocks = new BlockManager ();
268 this.blocks = blocks;
269 start = blocks.Position;
272 public override int Length {
273 get { return length; }
276 public unsafe int Write (byte [] buf, int offset, int count)
278 if (Expandable == false)
279 throw new Exception ("This should not happen.");
281 fixed (byte *p = &buf[0]) {
282 IntPtr p2 = new IntPtr (p + offset);
283 blocks.Write (p2, count);
290 public int Write (IntPtr ptr, int count)
292 if (Expandable == false)
293 throw new Exception ("This should not happen.");
295 blocks.Write (ptr, count);
300 public override void Dispose ()
305 public override void Send (HttpWorkerRequest wr)
310 blocks.Send (wr, start, length);
313 public override void Send (Stream stream)
318 blocks.Send (stream, start, length);
322 class BufferedFileBucket : Bucket {
327 public BufferedFileBucket (string f, long off, long len)
334 public override int Length {
335 get { return (int) length; }
338 public override void Send (HttpWorkerRequest wr)
340 wr.SendResponseFromFile (file, offset, length);
343 public override void Send (Stream stream)
345 using (FileStream fs = File.OpenRead (file)) {
346 byte [] buffer = new byte [Math.Min (fs.Length, 32*1024)];
348 long remain = fs.Length;
350 while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
352 stream.Write (buffer, 0, n);
357 public override string ToString ()
359 return "file " + file + " " + length.ToString () + " bytes from position " + offset.ToString ();
363 void AppendBucket (Bucket b)
365 if (first_bucket == null) {
366 cur_bucket = first_bucket = b;
375 // Nothing happens here, broken by requirement.
376 // See note at the start
378 public override void Flush ()
382 void SendChunkSize (long l, bool last)
389 string s = l.ToString ("x");
390 for (; i < s.Length; i++)
391 chunk_buffer [i] = (byte) s [i];
394 chunk_buffer [i++] = 13;
395 chunk_buffer [i++] = 10;
397 chunk_buffer [i++] = 13;
398 chunk_buffer [i++] = 10;
401 response.WorkerRequest.SendResponseFromMemory (chunk_buffer, i);
404 internal void Flush (HttpWorkerRequest wr, bool final_flush)
406 if (total == 0 && !final_flush)
409 if (response.use_chunked)
410 SendChunkSize (total, false);
412 for (Bucket b = first_bucket; b != null; b = b.Next) {
416 if (response.use_chunked) {
417 SendChunkSize (-1, false);
419 SendChunkSize (0, true);
422 wr.FlushResponse (final_flush);
427 internal int GetTotalLength ()
430 for (Bucket b = first_bucket; b != null; b = b.Next)
436 internal MemoryStream GetData ()
438 MemoryStream stream = new MemoryStream ();
439 for (Bucket b = first_bucket; b != null; b = b.Next)
444 public void WriteFile (string f, long offset, long length)
449 ByteBucket bb = cur_bucket as ByteBucket;
452 bb.Expandable = false;
453 bb = new ByteBucket (bb.blocks);
458 AppendBucket (new BufferedFileBucket (f, offset, length));
461 // Flush () is called from HttpResponse if needed (WriteFile/TransmitFile)
465 internal void ApplyFilter (bool close)
471 Bucket one = first_bucket;
472 first_bucket = null; // This will recreate new buckets for the filtered content
475 for (Bucket b = one; b != null; b = b.Next)
478 for (Bucket b = one; b != null; b = b.Next)
491 public void WritePtr (IntPtr ptr, int length)
496 bool buffering = response.BufferOutput;
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);
508 // Write to the filter, which will call us back, and then Flush
511 byte [] bytes = new byte [length];
512 Marshal.Copy (ptr, bytes, 0, length);
513 filter.Write (bytes, 0, length);
518 Flush (response.WorkerRequest, false);
523 public override void Write (byte [] buffer, int offset, int count)
525 bool buffering = response.BufferOutput;
527 throw new ArgumentNullException ("buffer");
529 int max_count = buffer.Length - offset;
530 if (offset < 0 || max_count <= 0)
531 throw new ArgumentOutOfRangeException ("offset");
533 throw new ArgumentOutOfRangeException ("count");
534 if (count > max_count)
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
545 wr.SendResponseFromMemory (buffer, count);
547 UnsafeWrite (wr, buffer, offset, count);
549 wr.FlushResponse (false);
551 // Write to the filter, which will call us back, and then Flush
554 filter.Write (buffer, offset, count);
558 Flush (response.WorkerRequest, false);
563 void UnsafeWrite (HttpWorkerRequest wr, byte [] buffer, int offset, int count)
568 byte[] copy = new byte[count];
569 Array.Copy(buffer, offset, copy, 0, count);
570 wr.SendResponseFromMemory (copy, count);
573 unsafe void UnsafeWrite (HttpWorkerRequest wr, byte [] buffer, int offset, int count)
575 fixed (byte *ptr = buffer) {
576 wr.SendResponseFromMemory ((IntPtr) (ptr + offset), count);
580 void AppendBuffer (byte [] buffer, int offset, int count)
582 if (!(cur_bucket is ByteBucket))
583 AppendBucket (new ByteBucket ());
586 ((ByteBucket) cur_bucket).Write (buffer, offset, count);
589 void AppendBuffer (IntPtr ptr, int count)
591 if (!(cur_bucket is ByteBucket))
592 AppendBucket (new ByteBucket ());
595 ((ByteBucket) cur_bucket).Write (ptr, count);
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).
603 internal void ReleaseResources (bool close_filter)
605 if (close_filter && filter != null) {
610 for (Bucket b = first_bucket; b != null; b = b.Next)
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.
623 ReleaseResources (false);
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];
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];
651 ((int*)dest) [0] = ((int*)src) [0];
657 ((byte*)dest) [0] = ((byte*)src) [0];
664 // Do not use directly. Use memcpy.
665 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
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];
676 ((short*)dest) [0] = ((short*)src) [0];
682 ((byte*)dest) [0] = ((byte*)src) [0];
685 // Do not use directly. Use memcpy.
686 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
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];
701 ((byte*)dest) [0] = ((byte*)src) [0];
702 ((byte*)dest) [1] = ((byte*)src) [1];
708 ((byte*)dest) [0] = ((byte*)src) [0];
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) {
723 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
724 ((short*)dest) [0] = ((short*)src) [0];
729 if ((((int)dest | (int)src) & 1) != 0) {
730 memcpy1 (dest, src, size);
733 if ((((int)dest | (int)src) & 2) != 0) {
734 memcpy2 (dest, src, size);
738 memcpy4 (dest, src, size);
741 public override bool CanRead {
747 public override bool CanSeek {
753 public override bool CanWrite {
759 const string notsupported = "HttpResponseStream is a forward, write-only stream";
761 public override long Length {
763 throw new NotSupportedException (notsupported);
767 public override long Position {
769 throw new NotSupportedException (notsupported);
772 throw new NotSupportedException (notsupported);
776 public override long Seek (long offset, SeekOrigin origin)
778 throw new NotSupportedException (notsupported);
781 public override void SetLength (long value)
783 throw new NotSupportedException (notsupported);
786 public override int Read (byte [] buffer, int offset, int count)
788 throw new NotSupportedException (notsupported);