Merge pull request #2152 from joelmartinez/mdoc-preserver
[mono.git] / mcs / class / System / System.Net / ResponseStream.cs
index 55a0830ce01a7bf082d60980293a9b816d8f4693..860509b9aceaa2776fb0bb105699e7a0d6fa528e 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
+
+#if SECURITY_DEP
+
 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 : Stream
@@ -75,27 +78,48 @@ namespace System.Net {
                {
                        if (disposed == false) {
                                disposed = true;
-                               if (response.HeadersSent == false)
-                                       response.SendHeaders (true);
-
-                               if (response.SendChunked && !trailer_sent) {
-                                       WriteChunkSize (0, 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)
+               {
+                       // 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);
-                       stream.Write (b, 0, b.Length);
+                       return Encoding.ASCII.GetBytes (str);
                }
 
                internal void InternalWrite (byte [] buffer, int offset, int count)
@@ -114,19 +138,33 @@ 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;
-                       try {
-                               if (chunked)
-                                       WriteChunkSize (count, false);
-                       } catch { }
-                       InternalWrite (buffer, offset, count);
-                       try {
-                               if (chunked)
-                                       stream.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,
@@ -135,13 +173,25 @@ 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 stream.BeginWrite (buffer, offset, count, cback, state);
                }