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 ();
192 memcpy (data + position, src, count);
196 public void Send (HttpWorkerRequest wr, int start, int end)
198 if (end - start <= 0)
201 wr.SendResponseFromMemory ((IntPtr) (data + start), end - start);
204 public void Send (Stream stream, int start, int end)
206 int len = end - start;
210 byte [] buffer = new byte [Math.Min (len, 32 * 1024)];
211 int size = buffer.Length;
213 Marshal.Copy ((IntPtr) (data + start), buffer, 0, size);
214 stream.Write (buffer, 0, size);
217 if (len > 0 && len < size)
222 public void Dispose ()
224 if ((IntPtr) data != IntPtr.Zero) {
225 Marshal.FreeHGlobal ((IntPtr) data);
226 data = (byte *) IntPtr.Zero;
232 abstract class Bucket {
235 public virtual void Dispose ()
239 public abstract void Send (HttpWorkerRequest wr);
240 public abstract void Send (Stream stream);
241 public abstract int Length { get; }
247 class ByteBucket : Bucket {
250 public BlockManager blocks;
251 public bool Expandable = true;
253 public ByteBucket () : this (null)
257 public ByteBucket (BlockManager blocks)
260 blocks = new BlockManager ();
262 this.blocks = blocks;
263 start = blocks.Position;
266 public override int Length {
267 get { return length; }
270 public unsafe int Write (byte [] buf, int offset, int count)
272 if (Expandable == false)
273 throw new Exception ("This should not happen.");
275 fixed (byte *p = &buf[0]) {
276 IntPtr p2 = new IntPtr (p + offset);
277 blocks.Write (p2, count);
284 public int Write (IntPtr ptr, int count)
286 if (Expandable == false)
287 throw new Exception ("This should not happen.");
289 blocks.Write (ptr, count);
294 public override void Dispose ()
299 public override void Send (HttpWorkerRequest wr)
304 blocks.Send (wr, start, length);
307 public override void Send (Stream stream)
312 blocks.Send (stream, start, length);
316 class BufferedFileBucket : Bucket {
321 public BufferedFileBucket (string f, long off, long len)
328 public override int Length {
329 get { return (int) length; }
332 public override void Send (HttpWorkerRequest wr)
334 wr.SendResponseFromFile (file, offset, length);
337 public override void Send (Stream stream)
339 using (FileStream fs = File.OpenRead (file)) {
340 byte [] buffer = new byte [Math.Min (fs.Length, 32*1024)];
342 long remain = fs.Length;
344 while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
346 stream.Write (buffer, 0, n);
351 public override string ToString ()
353 return "file " + file + " " + length.ToString () + " bytes from position " + offset.ToString ();
357 void AppendBucket (Bucket b)
359 if (first_bucket == null) {
360 cur_bucket = first_bucket = b;
369 // Nothing happens here, broken by requirement.
370 // See note at the start
372 public override void Flush ()
376 void SendChunkSize (long l, bool last)
383 string s = l.ToString ("x");
384 for (; i < s.Length; i++)
385 chunk_buffer [i] = (byte) s [i];
388 chunk_buffer [i++] = 13;
389 chunk_buffer [i++] = 10;
391 chunk_buffer [i++] = 13;
392 chunk_buffer [i++] = 10;
395 response.WorkerRequest.SendResponseFromMemory (chunk_buffer, i);
398 internal void Flush (HttpWorkerRequest wr, bool final_flush)
400 if (total == 0 && !final_flush)
403 if (response.use_chunked)
404 SendChunkSize (total, false);
406 for (Bucket b = first_bucket; b != null; b = b.Next) {
410 if (response.use_chunked) {
411 SendChunkSize (-1, false);
413 SendChunkSize (0, true);
416 wr.FlushResponse (final_flush);
421 internal int GetTotalLength ()
424 for (Bucket b = first_bucket; b != null; b = b.Next)
430 internal MemoryStream GetData ()
432 MemoryStream stream = new MemoryStream ();
433 for (Bucket b = first_bucket; b != null; b = b.Next)
438 public void WriteFile (string f, long offset, long length)
443 ByteBucket bb = cur_bucket as ByteBucket;
446 bb.Expandable = false;
447 bb = new ByteBucket (bb.blocks);
452 AppendBucket (new BufferedFileBucket (f, offset, length));
455 // Flush () is called from HttpResponse if needed (WriteFile/TransmitFile)
459 internal void ApplyFilter (bool close)
465 Bucket one = first_bucket;
466 first_bucket = null; // This will recreate new buckets for the filtered content
469 for (Bucket b = one; b != null; b = b.Next)
472 for (Bucket b = one; b != null; b = b.Next)
485 public void WritePtr (IntPtr ptr, int length)
490 bool buffering = response.BufferOutput;
493 // It does not matter whether we're in ApplyFilter or not
494 AppendBuffer (ptr, length);
495 } else if (filter == null || filtering) {
496 response.WriteHeaders (false);
497 HttpWorkerRequest wr = response.WorkerRequest;
498 // Direct write because not buffering
499 wr.SendResponseFromMemory (ptr, length);
500 wr.FlushResponse (false);
502 // Write to the filter, which will call us back, and then Flush
505 byte [] bytes = new byte [length];
506 Marshal.Copy (ptr, bytes, 0, length);
507 filter.Write (bytes, 0, length);
512 Flush (response.WorkerRequest, false);
517 public override void Write (byte [] buffer, int offset, int count)
519 bool buffering = response.BufferOutput;
521 throw new ArgumentNullException ("buffer");
523 int max_count = buffer.Length - offset;
524 if (offset < 0 || max_count <= 0)
525 throw new ArgumentOutOfRangeException ("offset");
527 throw new ArgumentOutOfRangeException ("count");
528 if (count > max_count)
532 // It does not matter whether we're in ApplyFilter or not
533 AppendBuffer (buffer, offset, count);
534 } else if (filter == null || filtering) {
535 response.WriteHeaders (false);
536 HttpWorkerRequest wr = response.WorkerRequest;
537 // Direct write because not buffering
539 wr.SendResponseFromMemory (buffer, count);
541 UnsafeWrite (wr, buffer, offset, count);
543 wr.FlushResponse (false);
545 // Write to the filter, which will call us back, and then Flush
548 filter.Write (buffer, offset, count);
552 Flush (response.WorkerRequest, false);
557 void UnsafeWrite (HttpWorkerRequest wr, byte [] buffer, int offset, int count)
562 byte[] copy = new byte[count];
563 Array.Copy(buffer, offset, copy, 0, count);
564 wr.SendResponseFromMemory (copy, count);
567 unsafe void UnsafeWrite (HttpWorkerRequest wr, byte [] buffer, int offset, int count)
569 fixed (byte *ptr = buffer) {
570 wr.SendResponseFromMemory ((IntPtr) (ptr + offset), count);
574 void AppendBuffer (byte [] buffer, int offset, int count)
576 if (!(cur_bucket is ByteBucket))
577 AppendBucket (new ByteBucket ());
580 ((ByteBucket) cur_bucket).Write (buffer, offset, count);
583 void AppendBuffer (IntPtr ptr, int count)
585 if (!(cur_bucket is ByteBucket))
586 AppendBucket (new ByteBucket ());
589 ((ByteBucket) cur_bucket).Write (ptr, count);
593 // This should not flush/close or anything else, its called
594 // just to free any memory we might have allocated (when we later
595 // implement something with unmanaged memory).
597 internal void ReleaseResources (bool close_filter)
599 if (close_filter && filter != null) {
604 for (Bucket b = first_bucket; b != null; b = b.Next)
614 // IMPORTANT: you must dispose *AFTER* using all the buckets Byte chunks might be
615 // split across two buckets if there is a file between the data.
617 ReleaseResources (false);
621 // Do not use directly. Use memcpy.
622 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
623 /*while (size >= 32) {
624 // using long is better than int and slower than double
625 // FIXME: enable this only on correct alignment or on platforms
626 // that can tolerate unaligned reads/writes of doubles
627 ((double*)dest) [0] = ((double*)src) [0];
628 ((double*)dest) [1] = ((double*)src) [1];
629 ((double*)dest) [2] = ((double*)src) [2];
630 ((double*)dest) [3] = ((double*)src) [3];
636 ((int*)dest) [0] = ((int*)src) [0];
637 ((int*)dest) [1] = ((int*)src) [1];
638 ((int*)dest) [2] = ((int*)src) [2];
639 ((int*)dest) [3] = ((int*)src) [3];
645 ((int*)dest) [0] = ((int*)src) [0];
651 ((byte*)dest) [0] = ((byte*)src) [0];
658 // Do not use directly. Use memcpy.
659 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
661 ((short*)dest) [0] = ((short*)src) [0];
662 ((short*)dest) [1] = ((short*)src) [1];
663 ((short*)dest) [2] = ((short*)src) [2];
664 ((short*)dest) [3] = ((short*)src) [3];
670 ((short*)dest) [0] = ((short*)src) [0];
676 ((byte*)dest) [0] = ((byte*)src) [0];
679 // Do not use directly. Use memcpy.
680 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
682 ((byte*)dest) [0] = ((byte*)src) [0];
683 ((byte*)dest) [1] = ((byte*)src) [1];
684 ((byte*)dest) [2] = ((byte*)src) [2];
685 ((byte*)dest) [3] = ((byte*)src) [3];
686 ((byte*)dest) [4] = ((byte*)src) [4];
687 ((byte*)dest) [5] = ((byte*)src) [5];
688 ((byte*)dest) [6] = ((byte*)src) [6];
689 ((byte*)dest) [7] = ((byte*)src) [7];
695 ((byte*)dest) [0] = ((byte*)src) [0];
696 ((byte*)dest) [1] = ((byte*)src) [1];
702 ((byte*)dest) [0] = ((byte*)src) [0];
705 static unsafe void memcpy (byte *dest, byte *src, int size) {
706 // FIXME: if pointers are not aligned, try to align them
707 // so a faster routine can be used. Handle the case where
708 // the pointers can't be reduced to have the same alignment
709 // (just ignore the issue on x86?)
710 if ((((int)dest | (int)src) & 3) != 0) {
711 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
717 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
718 ((short*)dest) [0] = ((short*)src) [0];
723 if ((((int)dest | (int)src) & 1) != 0) {
724 memcpy1 (dest, src, size);
727 if ((((int)dest | (int)src) & 2) != 0) {
728 memcpy2 (dest, src, size);
732 memcpy4 (dest, src, size);
735 public override bool CanRead {
741 public override bool CanSeek {
747 public override bool CanWrite {
753 const string notsupported = "HttpResponseStream is a forward, write-only stream";
755 public override long Length {
757 throw new NotSupportedException (notsupported);
761 public override long Position {
763 throw new NotSupportedException (notsupported);
766 throw new NotSupportedException (notsupported);
770 public override long Seek (long offset, SeekOrigin origin)
772 throw new NotSupportedException (notsupported);
775 public override void SetLength (long value)
777 throw new NotSupportedException (notsupported);
780 public override int Read (byte [] buffer, int offset, int count)
782 throw new NotSupportedException (notsupported);