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 int Write (byte [] buf, int offset, int count)
271 if (Expandable == false)
272 throw new Exception ("This should not happen.");
274 blocks.Write (buf, offset, count);
279 public int Write (IntPtr ptr, int count)
281 if (Expandable == false)
282 throw new Exception ("This should not happen.");
284 blocks.Write (ptr, count);
289 public override void Dispose ()
294 public override void Send (HttpWorkerRequest wr)
299 blocks.Send (wr, start, length);
302 public override void Send (Stream stream)
307 blocks.Send (stream, start, length);
311 class BufferedFileBucket : Bucket {
316 public BufferedFileBucket (string f, long off, long len)
323 public override int Length {
324 get { return (int) length; }
327 public override void Send (HttpWorkerRequest wr)
329 wr.SendResponseFromFile (file, offset, length);
332 public override void Send (Stream stream)
334 using (FileStream fs = File.OpenRead (file)) {
335 byte [] buffer = new byte [Math.Min (fs.Length, 32*1024)];
337 long remain = fs.Length;
339 while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32*1024))) != 0){
341 stream.Write (buffer, 0, n);
346 public override string ToString ()
348 return "file " + file + " " + length.ToString () + " bytes from position " + offset.ToString ();
352 void AppendBucket (Bucket b)
354 if (first_bucket == null) {
355 cur_bucket = first_bucket = b;
364 // Nothing happens here, broken by requirement.
365 // See note at the start
367 public override void Flush ()
371 void SendChunkSize (long l, bool last)
378 string s = l.ToString ("x");
379 for (; i < s.Length; i++)
380 chunk_buffer [i] = (byte) s [i];
383 chunk_buffer [i++] = 13;
384 chunk_buffer [i++] = 10;
386 chunk_buffer [i++] = 13;
387 chunk_buffer [i++] = 10;
390 response.WorkerRequest.SendResponseFromMemory (chunk_buffer, i);
393 internal void Flush (HttpWorkerRequest wr, bool final_flush)
395 if (total == 0 && !final_flush)
398 if (response.use_chunked)
399 SendChunkSize (total, false);
401 for (Bucket b = first_bucket; b != null; b = b.Next) {
405 if (response.use_chunked) {
406 SendChunkSize (-1, false);
408 SendChunkSize (0, true);
411 wr.FlushResponse (final_flush);
416 internal int GetTotalLength ()
419 for (Bucket b = first_bucket; b != null; b = b.Next)
425 internal MemoryStream GetData ()
427 MemoryStream stream = new MemoryStream ();
428 for (Bucket b = first_bucket; b != null; b = b.Next)
433 public void WriteFile (string f, long offset, long length)
438 ByteBucket bb = cur_bucket as ByteBucket;
441 bb.Expandable = false;
442 bb = new ByteBucket (bb.blocks);
447 AppendBucket (new BufferedFileBucket (f, offset, length));
450 // Flush () is called from HttpResponse if needed (WriteFile/TransmitFile)
454 internal void ApplyFilter (bool close)
460 Bucket one = first_bucket;
461 first_bucket = null; // This will recreate new buckets for the filtered content
464 for (Bucket b = one; b != null; b = b.Next)
467 for (Bucket b = one; b != null; b = b.Next)
480 public void WritePtr (IntPtr ptr, int length)
485 bool buffering = response.BufferOutput;
488 // It does not matter whether we're in ApplyFilter or not
489 AppendBuffer (ptr, length);
490 } else if (filter == null || filtering) {
491 response.WriteHeaders (false);
492 HttpWorkerRequest wr = response.WorkerRequest;
493 // Direct write because not buffering
494 wr.SendResponseFromMemory (ptr, length);
495 wr.FlushResponse (false);
497 // Write to the filter, which will call us back, and then Flush
500 byte [] bytes = new byte [length];
501 Marshal.Copy (ptr, bytes, 0, length);
502 filter.Write (bytes, 0, length);
507 Flush (response.WorkerRequest, false);
512 public override void Write (byte [] buffer, int offset, int count)
514 bool buffering = response.BufferOutput;
517 // It does not matter whether we're in ApplyFilter or not
518 AppendBuffer (buffer, offset, count);
519 } else if (filter == null || filtering) {
520 response.WriteHeaders (false);
521 HttpWorkerRequest wr = response.WorkerRequest;
522 // Direct write because not buffering
524 wr.SendResponseFromMemory (buffer, count);
526 UnsafeWrite (wr, buffer, offset, count);
528 wr.FlushResponse (false);
530 // Write to the filter, which will call us back, and then Flush
533 filter.Write (buffer, offset, count);
537 Flush (response.WorkerRequest, false);
542 void UnsafeWrite (HttpWorkerRequest wr, byte [] buffer, int offset, int count)
547 byte[] copy = new byte[count];
548 Array.Copy(buffer, offset, copy, 0, count);
549 wr.SendResponseFromMemory (copy, count);
552 unsafe void UnsafeWrite (HttpWorkerRequest wr, byte [] buffer, int offset, int count)
554 fixed (byte *ptr = buffer) {
555 wr.SendResponseFromMemory ((IntPtr) (ptr + offset), count);
559 void AppendBuffer (byte [] buffer, int offset, int count)
561 if (!(cur_bucket is ByteBucket))
562 AppendBucket (new ByteBucket ());
565 ((ByteBucket) cur_bucket).Write (buffer, offset, count);
568 void AppendBuffer (IntPtr ptr, int count)
570 if (!(cur_bucket is ByteBucket))
571 AppendBucket (new ByteBucket ());
574 ((ByteBucket) cur_bucket).Write (ptr, count);
578 // This should not flush/close or anything else, its called
579 // just to free any memory we might have allocated (when we later
580 // implement something with unmanaged memory).
582 internal void ReleaseResources (bool close_filter)
584 if (close_filter && filter != null) {
589 for (Bucket b = first_bucket; b != null; b = b.Next)
599 // IMPORTANT: you must dispose *AFTER* using all the buckets Byte chunks might be
600 // split across two buckets if there is a file between the data.
602 ReleaseResources (false);
606 // Do not use directly. Use memcpy.
607 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
608 /*while (size >= 32) {
609 // using long is better than int and slower than double
610 // FIXME: enable this only on correct alignment or on platforms
611 // that can tolerate unaligned reads/writes of doubles
612 ((double*)dest) [0] = ((double*)src) [0];
613 ((double*)dest) [1] = ((double*)src) [1];
614 ((double*)dest) [2] = ((double*)src) [2];
615 ((double*)dest) [3] = ((double*)src) [3];
621 ((int*)dest) [0] = ((int*)src) [0];
622 ((int*)dest) [1] = ((int*)src) [1];
623 ((int*)dest) [2] = ((int*)src) [2];
624 ((int*)dest) [3] = ((int*)src) [3];
630 ((int*)dest) [0] = ((int*)src) [0];
636 ((byte*)dest) [0] = ((byte*)src) [0];
643 // Do not use directly. Use memcpy.
644 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
646 ((short*)dest) [0] = ((short*)src) [0];
647 ((short*)dest) [1] = ((short*)src) [1];
648 ((short*)dest) [2] = ((short*)src) [2];
649 ((short*)dest) [3] = ((short*)src) [3];
655 ((short*)dest) [0] = ((short*)src) [0];
661 ((byte*)dest) [0] = ((byte*)src) [0];
664 // Do not use directly. Use memcpy.
665 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
667 ((byte*)dest) [0] = ((byte*)src) [0];
668 ((byte*)dest) [1] = ((byte*)src) [1];
669 ((byte*)dest) [2] = ((byte*)src) [2];
670 ((byte*)dest) [3] = ((byte*)src) [3];
671 ((byte*)dest) [4] = ((byte*)src) [4];
672 ((byte*)dest) [5] = ((byte*)src) [5];
673 ((byte*)dest) [6] = ((byte*)src) [6];
674 ((byte*)dest) [7] = ((byte*)src) [7];
680 ((byte*)dest) [0] = ((byte*)src) [0];
681 ((byte*)dest) [1] = ((byte*)src) [1];
687 ((byte*)dest) [0] = ((byte*)src) [0];
690 static unsafe void memcpy (byte *dest, byte *src, int size) {
691 // FIXME: if pointers are not aligned, try to align them
692 // so a faster routine can be used. Handle the case where
693 // the pointers can't be reduced to have the same alignment
694 // (just ignore the issue on x86?)
695 if ((((int)dest | (int)src) & 3) != 0) {
696 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
702 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
703 ((short*)dest) [0] = ((short*)src) [0];
708 if ((((int)dest | (int)src) & 1) != 0) {
709 memcpy1 (dest, src, size);
712 if ((((int)dest | (int)src) & 2) != 0) {
713 memcpy2 (dest, src, size);
717 memcpy4 (dest, src, size);
720 public override bool CanRead {
726 public override bool CanSeek {
731 public override bool CanWrite {
737 const string notsupported = "HttpResponseStream is a forward, write-only stream";
739 public override long Length {
741 throw new InvalidOperationException (notsupported);
745 public override long Position {
747 throw new InvalidOperationException (notsupported);
750 throw new InvalidOperationException (notsupported);
754 public override long Seek (long offset, SeekOrigin origin)
756 throw new InvalidOperationException (notsupported);
759 public override void SetLength (long value)
761 throw new InvalidOperationException (notsupported);
764 public override int Read (byte [] buffer, int offset, int count)
766 throw new InvalidOperationException (notsupported);