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;
42 // HttpResponseStream implements the "OutputStream" from HttpResponse
44 // The MS implementation is broken in that it does not hook up this
45 // to HttpResponse, so calling "Flush" on Response.OutputStream does not
46 // flush the contents and produce the headers.
48 // You must call HttpResponse.Flush which does the actual header generation
49 // and actual data flushing
51 internal class HttpResponseStream : Stream
55 HttpResponse response;
60 public HttpResponseStream (HttpResponse response)
62 this.response = response;
65 internal bool HaveFilter
67 get { return filter != null; }
75 filter = new OutputFilterStream (this);
87 public virtual void Dispose ()
91 public abstract void Send (HttpWorkerRequest wr);
92 public abstract void Send (Stream stream);
93 public abstract int Length { get; }
94 public abstract int FreeSpace { get; }
97 class ByteBucket : Bucket
100 const int _preferredLength = 16 * 1024;
103 int _freeSpace = _preferredLength;
104 byte [] buffer = new byte [_preferredLength];
110 public override int Length
112 get { return _position; }
115 public override int FreeSpace
117 get { return _freeSpace; }
120 public int Write (byte [] buf, int offset, int count)
122 if (count > _freeSpace)
123 throw new InvalidOperationException ("Out of bucket space");
125 Array.Copy (buf, offset, buffer, _position, count);
127 _freeSpace = _preferredLength - _position;
131 public override void Dispose ()
136 public override void Send (HttpWorkerRequest wr)
141 wr.SendResponseFromMemory (buffer, _position);
144 public override void Send (Stream stream)
149 stream.Write (buffer, 0, _position);
153 class CharBucket : Bucket
155 const int _preferredLength = 8 * 1024;
158 int _freeSpace = _preferredLength;
159 char [] buffer = new char [_preferredLength];
165 public override int Length
169 HttpContext current = HttpContext.Current;
170 Encoding enc = (current != null) ? current.Response.ContentEncoding : Encoding.UTF8;
171 return enc.GetByteCount (buffer, 0, _position);
175 public override int FreeSpace
177 get { return _freeSpace; }
180 public int Write (string buf, int offset, int count)
182 if (count > _freeSpace)
183 throw new InvalidOperationException ("Out of bucket space");
185 buf.CopyTo (offset, buffer, _position, count);
188 _freeSpace = _preferredLength - _position;
192 public int Write (char [] buf, int offset, int count)
194 if (count > _freeSpace)
195 throw new InvalidOperationException ("Out of bucket space");
198 buffer [_position] = buf [offset];
200 Array.Copy (buf, offset, buffer, _position, count);
203 _freeSpace = _preferredLength - _position;
207 public override void Dispose ()
212 public override void Send (HttpWorkerRequest wr)
217 wr.SendResponseFromMemory (buffer, _position);
220 public override void Send (Stream stream)
225 StreamWriter writer = new StreamWriter (stream);
226 writer.Write (buffer, 0, _position);
230 class BufferedFileBucket : Bucket
236 public BufferedFileBucket (string f, long off, long len)
243 public override int Length
245 get { return (int) length; }
248 public override int FreeSpace
250 get { return int.MaxValue; }
253 public override void Send (HttpWorkerRequest wr)
255 wr.SendResponseFromFile (file, offset, length);
258 public override void Send (Stream stream)
260 using (FileStream fs = File.OpenRead (file)) {
261 byte [] buffer = new byte [Math.Min (fs.Length, 32 * 1024)];
263 long remain = fs.Length;
265 while (remain > 0 && (n = fs.Read (buffer, 0, (int) Math.Min (remain, 32 * 1024))) != 0) {
267 stream.Write (buffer, 0, n);
272 public override string ToString ()
274 return String.Format ("file {0} {1} bytes from position {2}", file, length, offset);
278 void AppendBucket (Bucket b)
280 if (first_bucket == null) {
281 cur_bucket = first_bucket = b;
290 // Nothing happens here, broken by requirement.
291 // See note at the start
293 public override void Flush ()
297 internal void Flush (HttpWorkerRequest wr, bool final_flush)
299 if (!dirty && !final_flush)
302 for (Bucket b = first_bucket; b != null; b = b.Next) {
306 wr.FlushResponse (final_flush);
310 internal int GetTotalLength ()
313 for (Bucket b = first_bucket; b != null; b = b.Next)
319 internal MemoryStream GetData ()
321 MemoryStream stream = new MemoryStream ();
322 for (Bucket b = first_bucket; b != null; b = b.Next)
327 public void WriteFile (string f, long offset, long length)
334 AppendBucket (new BufferedFileBucket (f, offset, length));
335 // Flush () is called from HttpResponse if needed (WriteFile/TransmitFile)
339 internal void ApplyFilter (bool close)
345 Bucket one = first_bucket;
346 first_bucket = null; // This will recreate new buckets for the filtered content
349 for (Bucket b = one; b != null; b = b.Next)
352 for (Bucket b = one; b != null; b = b.Next)
366 public void Write (char [] buffer, int offset, int count)
368 bool buffering = response.BufferOutput;
371 // It does not matter whether we're in ApplyFilter or not
372 AppendBuffer (buffer, offset, count);
374 else if (filter == null || filtering) {
375 response.WriteHeaders (false);
376 HttpWorkerRequest wr = response.WorkerRequest;
377 // Direct write because not buffering
379 wr.SendResponseFromMemory (buffer, count);
382 UnsafeWrite (wr, buffer, offset, count);
384 wr.FlushResponse (false);
387 // Write to the filter, which will call us back, and then Flush
390 StreamWriter wr = new StreamWriter (filter, response.ContentEncoding);
391 wr.Write (buffer, offset, count);
396 Flush (response.WorkerRequest, false);
400 public void Write (string s, int offset, int count)
402 bool buffering = response.BufferOutput;
405 // It does not matter whether we're in ApplyFilter or not
406 AppendBuffer (s, offset, count);
408 else if (filter == null || filtering) {
409 response.WriteHeaders (false);
410 HttpWorkerRequest wr = response.WorkerRequest;
411 // Direct write because not buffering
413 wr.SendResponseFromMemory (s.ToCharArray (), count);
416 UnsafeWrite (wr, s.ToCharArray (), offset, count);
418 wr.FlushResponse (false);
421 // Write to the filter, which will call us back, and then Flush
424 StreamWriter wr = new StreamWriter (filter, response.ContentEncoding);
425 wr.Write (s, offset, count);
430 Flush (response.WorkerRequest, false);
434 public override void Write (byte [] buffer, int offset, int count)
436 bool buffering = response.BufferOutput;
439 // It does not matter whether we're in ApplyFilter or not
440 AppendBuffer (buffer, offset, count);
442 else if (filter == null || filtering) {
443 response.WriteHeaders (false);
444 HttpWorkerRequest wr = response.WorkerRequest;
445 // Direct write because not buffering
447 wr.SendResponseFromMemory (buffer, count);
450 UnsafeWrite (wr, buffer, offset, count);
452 wr.FlushResponse (false);
455 // Write to the filter, which will call us back, and then Flush
458 filter.Write (buffer, offset, count);
463 Flush (response.WorkerRequest, false);
468 void UnsafeWrite (HttpWorkerRequest wr, byte [] buffer, int offset, int count)
473 byte [] copy = new byte [count];
474 Array.Copy (buffer, offset, copy, 0, count);
475 wr.SendResponseFromMemory (copy, count);
477 void UnsafeWrite (HttpWorkerRequest wr, char [] buffer, int offset, int count)
482 char [] copy = new char [count];
483 Array.Copy (buffer, offset, copy, 0, count);
484 wr.SendResponseFromMemory (copy, count);
487 unsafe void UnsafeWrite (HttpWorkerRequest wr, byte [] buffer, int offset, int count)
489 fixed (byte *ptr = buffer) {
490 wr.SendResponseFromMemory ((IntPtr) (ptr + offset), count);
494 void AppendBuffer (byte [] buffer, int offset, int count)
496 if (!(cur_bucket is ByteBucket))
497 AppendBucket (new ByteBucket ());
502 if (cur_bucket.FreeSpace == 0)
503 AppendBucket (new ByteBucket ());
506 int freeSpace = cur_bucket.FreeSpace;
511 ((ByteBucket) cur_bucket).Write (buffer, offset, len);
518 void AppendBuffer (char [] buffer, int offset, int count)
520 if (!(cur_bucket is CharBucket))
521 AppendBucket (new CharBucket ());
526 if (cur_bucket.FreeSpace == 0)
527 AppendBucket (new CharBucket ());
530 int freeSpace = cur_bucket.FreeSpace;
535 ((CharBucket) cur_bucket).Write (buffer, offset, len);
541 void AppendBuffer (string buffer, int offset, int count)
543 if (!(cur_bucket is CharBucket))
544 AppendBucket (new CharBucket ());
549 if (cur_bucket.FreeSpace == 0)
550 AppendBucket (new CharBucket ());
553 int freeSpace = cur_bucket.FreeSpace;
558 ((CharBucket) cur_bucket).Write (buffer, offset, len);
565 // This should not flush/close or anything else, its called
566 // just to free any memory we might have allocated (when we later
567 // implement something with unmanaged memory).
569 internal void ReleaseResources (bool close_filter)
571 if (close_filter && filter != null) {
576 for (Bucket b = first_bucket; b != null; b = b.Next)
586 // IMPORTANT: you must dispose *AFTER* using all the buckets Byte chunks might be
587 // split across two buckets if there is a file between the data.
589 ReleaseResources (false);
593 public override bool CanRead
601 public override bool CanSeek
608 public override bool CanWrite
616 const string notsupported = "HttpResponseStream is a forward, write-only stream";
618 public override long Length
622 throw new InvalidOperationException (notsupported);
626 public override long Position
630 throw new InvalidOperationException (notsupported);
634 throw new InvalidOperationException (notsupported);
638 public override long Seek (long offset, SeekOrigin origin)
640 throw new InvalidOperationException (notsupported);
643 public override void SetLength (long value)
645 throw new InvalidOperationException (notsupported);
648 public override int Read (byte [] buffer, int offset, int count)
650 throw new InvalidOperationException (notsupported);