[Mono.Security]: Add 'MonoTlsProvider.SupportsCleanShutdown' and 'MonoTlsSettings...
[mono.git] / mcs / class / System / System.Net / ResponseStream.cs
index 104aef3173deb13bf077c8f3c0fed5135b2ecc2f..5d33e477d1c2b7d733188dfe3ee309fdc07ec1e8 100644 (file)
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-#if NET_2_0
+
 using System.IO;
 using System.Net.Sockets;
 using System.Text;
 using System.Runtime.InteropServices;
 namespace System.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 : NetworkStream
+       class ResponseStream : Stream
        {
                HttpListenerResponse response;
                bool ignore_errors;
                bool disposed;
                bool trailer_sent;
+               Stream stream;
 
-               internal ResponseStream (Socket sock, HttpListenerResponse response, bool ignore_errors) :
-                                       base (sock, false)
+               internal ResponseStream (Stream stream, HttpListenerResponse response, bool ignore_errors)
                {
                        this.response = response;
                        this.ignore_errors = ignore_errors;
+                       this.stream = stream;
                }
 
                public override bool CanRead {
@@ -74,37 +76,64 @@ namespace System.Net {
                {
                        if (disposed == false) {
                                disposed = true;
-                               if (response.HeadersSent == false)
-                                       response.SendHeaders (true);
-
-                               if (response.SendChunked && !trailer_sent) {
-                                       WriteChunkSize (0, true);
-                                       trailer_sent = true;
+                               byte [] bytes = null;
+                               MemoryStream ms = GetHeaders (true);
+                               bool chunked = response.SendChunked;
+                               if (stream.CanWrite) {
+                                       try {
+                                               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;
+                                               }
+                                       } catch (IOException) {
+                                               // Ignore error due to connection reset by peer
+                                       }
                                }
                                response.Close ();
                        }
                }
 
+               MemoryStream GetHeaders (bool closing)
+               {
+                       // SendHeaders works on shared headers
+                       lock (response.headers_lock) {
+                               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 };
-               void WriteChunkSize (int size, bool final)
+               static byte [] GetChunkSizeBytes (int size, bool final)
                {
                        string str = String.Format ("{0:x}\r\n{1}", size, final ? "\r\n" : "");
-                       byte [] b = Encoding.ASCII.GetBytes (str);
-                       base.Write (b, 0, b.Length);
+                       return Encoding.ASCII.GetBytes (str);
                }
 
                internal void InternalWrite (byte [] buffer, int offset, int count)
                {
                        if (ignore_errors) {
                                try {
-                                       base.Write (buffer, offset, count);
+                                       stream.Write (buffer, offset, count);
                                } catch { }
                        } else {
-                               base.Write (buffer, offset, count);
+                               stream.Write (buffer, offset, count);
                        }
                }
 
@@ -112,20 +141,36 @@ namespace System.Net {
                {
                        if (disposed)
                                throw new ObjectDisposedException (GetType ().ToString ());
+                       if (count == 0)
+                               return;
 
-                       if (response.HeadersSent == false)
-                               response.SendHeaders (false);
-
+                       byte [] bytes = null;
+                       MemoryStream ms = GetHeaders (false);
                        bool chunked = response.SendChunked;
-                       try {
-                               if (chunked)
-                                       WriteChunkSize (count, false);
-                       } catch { }
-                       InternalWrite (buffer, offset, count);
-                       try {
-                               if (chunked)
-                                       base.Write (crlf, 0, 2);
-                       } catch { }
+                       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,
@@ -134,14 +179,26 @@ namespace System.Net {
                        if (disposed)
                                throw new ObjectDisposedException (GetType ().ToString ());
 
-                       if (response.HeadersSent == false)
-                               response.SendHeaders (false);
+                       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);
+                       }
 
-                       try {
-                               if (response.SendChunked)
-                                       WriteChunkSize (count, false);
-                       } catch { }
-                       return base.BeginWrite (buffer, offset, count, cback, state);
+                       return stream.BeginWrite (buffer, offset, count, cback, state);
                }
 
                public override void EndWrite (IAsyncResult ares)
@@ -151,14 +208,14 @@ namespace System.Net {
 
                        if (ignore_errors) {
                                try {
-                                       base.EndWrite (ares);
+                                       stream.EndWrite (ares);
                                        if (response.SendChunked)
-                                               base.Write (crlf, 0, 2);
+                                               stream.Write (crlf, 0, 2);
                                } catch { }
                        } else {
-                               base.EndWrite (ares);
+                               stream.EndWrite (ares);
                                if (response.SendChunked)
-                                       base.Write (crlf, 0, 2);
+                                       stream.Write (crlf, 0, 2);
                        }
                }
 
@@ -189,5 +246,4 @@ namespace System.Net {
                }
        }
 }
-#endif