| // | 
| // ResponseStream.cs | 
| //    Copied from System.Net.ResponseStream | 
| // | 
| // Author: | 
| //    Gonzalo Paniagua Javier (gonzalo@novell.com) | 
| // | 
| // Copyright (c) 2005 Novell, Inc. (http://www.novell.com) | 
| // | 
| // Permission is hereby granted, free of charge, to any person obtaining | 
| // a copy of this software and associated documentation files (the | 
| // "Software"), to deal in the Software without restriction, including | 
| // without limitation the rights to use, copy, modify, merge, publish, | 
| // distribute, sublicense, and/or sell copies of the Software, and to | 
| // permit persons to whom the Software is furnished to do so, subject to | 
| // the following conditions: | 
| //  | 
| // The above copyright notice and this permission notice shall be | 
| // included in all copies or substantial portions of the Software. | 
| //  | 
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
| // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 
| // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | 
| // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | 
| // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | 
| // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 
| // | 
|   | 
| using System; | 
| using System.IO; | 
| using System.Net; | 
| using System.Net.Sockets; | 
| using System.Text; | 
| using System.Runtime.InteropServices; | 
|   | 
| namespace WebSocketSharp.Net { | 
|   | 
|     // FIXME: Does this buffer the response until Close? | 
|     // Update: we send a single packet for the first non-chunked Write | 
|     // What happens when we set content-length to X and write X-1 bytes then close? | 
|     // what if we don't set content-length at all? | 
|     class ResponseStream : Stream | 
|     { | 
|   | 
|         HttpListenerResponse response; | 
|         bool ignore_errors; | 
|         bool disposed; | 
|         bool trailer_sent; | 
|         System.IO.Stream stream; | 
|   | 
|         internal ResponseStream (System.IO.Stream stream, HttpListenerResponse response, bool ignore_errors) | 
|         { | 
|             this.response = response; | 
|             this.ignore_errors = ignore_errors; | 
|             this.stream = stream; | 
|         } | 
|   | 
|         public override bool CanRead { | 
|             get { return false; } | 
|         } | 
|   | 
|         public override bool CanSeek { | 
|             get { return false; } | 
|         } | 
|   | 
|         public override bool CanWrite { | 
|             get { return true; } | 
|         } | 
|   | 
|         public override long Length { | 
|             get { throw new NotSupportedException (); } | 
|         } | 
|   | 
|         public override long Position { | 
|             get { throw new NotSupportedException (); } | 
|             set { throw new NotSupportedException (); } | 
|         } | 
|   | 
|   | 
|         public override void Close () | 
|         { | 
|             if (disposed == false) { | 
|                 disposed = true; | 
|                 byte [] bytes = null; | 
|                 MemoryStream ms = GetHeaders (true); | 
|                 bool chunked = response.SendChunked; | 
|                 if (ms != null) { | 
|                     long start = ms.Position; | 
|                     if (chunked && !trailer_sent) { | 
|                         bytes = GetChunkSizeBytes (0, true); | 
|                         ms.Position = ms.Length; | 
|                         ms.Write (bytes, 0, bytes.Length); | 
|                     } | 
|                     InternalWrite (ms.GetBuffer (), (int) start, (int) (ms.Length - start)); | 
|                     trailer_sent = true; | 
|                 } else if (chunked && !trailer_sent) { | 
|                     bytes = GetChunkSizeBytes (0, true); | 
|                     InternalWrite (bytes, 0, bytes.Length); | 
|                     trailer_sent = true; | 
|                 } | 
|                 response.Close (); | 
|             } | 
|         } | 
|   | 
|         MemoryStream GetHeaders (bool closing) | 
|         { | 
|             if (response.HeadersSent) | 
|                 return null; | 
|             MemoryStream ms = new MemoryStream (); | 
|             response.SendHeaders (closing, ms); | 
|             return ms; | 
|         } | 
|   | 
|         public override void Flush () | 
|         { | 
|         } | 
|   | 
|         static byte [] crlf = new byte [] { 13, 10 }; | 
|         static byte [] GetChunkSizeBytes (int size, bool final) | 
|         { | 
|             string str = String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : ""); | 
|             return Encoding.ASCII.GetBytes (str); | 
|         } | 
|   | 
|         internal void InternalWrite (byte [] buffer, int offset, int count) | 
|         { | 
|             if (ignore_errors) { | 
|                 try { | 
|                     stream.Write (buffer, offset, count); | 
|                 } catch { } | 
|             } else { | 
|                 stream.Write (buffer, offset, count); | 
|             } | 
|         } | 
|   | 
|         public override void Write (byte [] buffer, int offset, int count) | 
|         { | 
|             if (disposed) | 
|                 throw new ObjectDisposedException (GetType ().ToString ()); | 
|   | 
|             byte [] bytes = null; | 
|             MemoryStream ms = GetHeaders (false); | 
|             bool chunked = response.SendChunked; | 
|             if (ms != null) { | 
|                 long start = ms.Position; // After the possible preamble for the encoding | 
|                 ms.Position = ms.Length; | 
|                 if (chunked) { | 
|                     bytes = GetChunkSizeBytes (count, false); | 
|                     ms.Write (bytes, 0, bytes.Length); | 
|                 } | 
|   | 
|                 int new_count = Math.Min (count, 16384 - (int) ms.Position + (int) start); | 
|                 ms.Write (buffer, offset, new_count); | 
|                 count -= new_count; | 
|                 offset += new_count; | 
|                 InternalWrite (ms.GetBuffer (), (int) start, (int) (ms.Length - start)); | 
|                 ms.SetLength (0); | 
|                 ms.Capacity = 0; // 'dispose' the buffer in ms. | 
|             } else if (chunked) { | 
|                 bytes = GetChunkSizeBytes (count, false); | 
|                 InternalWrite (bytes, 0, bytes.Length); | 
|             } | 
|   | 
|             if (count > 0) | 
|                 InternalWrite (buffer, offset, count); | 
|             if (chunked) | 
|                 InternalWrite (crlf, 0, 2); | 
|         } | 
|   | 
|         public override IAsyncResult BeginWrite (byte [] buffer, int offset, int count, | 
|                             AsyncCallback cback, object state) | 
|         { | 
|             if (disposed) | 
|                 throw new ObjectDisposedException (GetType ().ToString ()); | 
|   | 
|             byte [] bytes = null; | 
|             MemoryStream ms = GetHeaders (false); | 
|             bool chunked = response.SendChunked; | 
|             if (ms != null) { | 
|                 long start = ms.Position; | 
|                 ms.Position = ms.Length; | 
|                 if (chunked) { | 
|                     bytes = GetChunkSizeBytes (count, false); | 
|                     ms.Write (bytes, 0, bytes.Length); | 
|                 } | 
|                 ms.Write (buffer, offset, count); | 
|                 buffer = ms.GetBuffer (); | 
|                 offset = (int) start; | 
|                 count = (int) (ms.Position - start); | 
|             } else if (chunked) { | 
|                 bytes = GetChunkSizeBytes (count, false); | 
|                 InternalWrite (bytes, 0, bytes.Length); | 
|             } | 
|   | 
|             return stream.BeginWrite (buffer, offset, count, cback, state); | 
|         } | 
|   | 
|         public override void EndWrite (IAsyncResult ares) | 
|         { | 
|             if (disposed) | 
|                 throw new ObjectDisposedException (GetType ().ToString ()); | 
|   | 
|             if (ignore_errors) { | 
|                 try { | 
|                     stream.EndWrite (ares); | 
|                     if (response.SendChunked) | 
|                         stream.Write (crlf, 0, 2); | 
|                 } catch { } | 
|             } else { | 
|                 stream.EndWrite (ares); | 
|                 if (response.SendChunked) | 
|                     stream.Write (crlf, 0, 2); | 
|             } | 
|         } | 
|   | 
|         public override int Read ([In,Out] byte[] buffer, int offset, int count) | 
|         { | 
|             throw new NotSupportedException (); | 
|         } | 
|   | 
|         public override IAsyncResult BeginRead (byte [] buffer, int offset, int count, | 
|                             AsyncCallback cback, object state) | 
|         { | 
|             throw new NotSupportedException (); | 
|         } | 
|   | 
|         public override int EndRead (IAsyncResult ares) | 
|         { | 
|             throw new NotSupportedException (); | 
|         } | 
|   | 
|         public override long Seek (long offset, SeekOrigin origin) | 
|         { | 
|             throw new NotSupportedException (); | 
|         } | 
|   | 
|         public override void SetLength (long value) | 
|         { | 
|             throw new NotSupportedException (); | 
|         } | 
|     } | 
| } |