Merge pull request #1404 from woodsb02/mono-route
[mono.git] / mcs / class / System.Net.Http / System.Net.Http / HttpContent.cs
index 89979a544b6c4aabdeae81aaed0150019f2e4b32..344d87164eb1621a6d55bc6d67d03c3def40488b 100644 (file)
@@ -35,7 +35,37 @@ namespace System.Net.Http
 {
        public abstract class HttpContent : IDisposable
        {
-               MemoryStream buffer;
+               sealed class FixedMemoryStream : MemoryStream
+               {
+                       readonly long maxSize;
+                       
+                       public FixedMemoryStream (long maxSize)
+                               : base ()
+                       {
+                               this.maxSize = maxSize;
+                       }
+                       
+                       void CheckOverflow (int count)
+                       {
+                               if (Length + count > maxSize)
+                                       throw new HttpRequestException (string.Format ("Cannot write more bytes to the buffer than the configured maximum buffer size: {0}", maxSize));
+                       }
+                       
+                       public override void WriteByte (byte value)
+                       {
+                               CheckOverflow (1);
+                               base.WriteByte (value);
+                       }
+                       
+                       public override void Write (byte[] buffer, int offset, int count)
+                       {
+                               CheckOverflow (count);
+                               base.Write (buffer, offset, count);
+                       }
+               }
+               
+               FixedMemoryStream buffer;
+               Stream stream;
                bool disposed;
                HttpContentHeaders headers;
 
@@ -45,6 +75,12 @@ namespace System.Net.Http
                        }
                }
 
+               internal long? LoadedBufferLength {
+                       get {
+                               return buffer == null ? (long?)null : buffer.Length;
+                       }
+               }
+
                public Task CopyToAsync (Stream stream)
                {
                        return CopyToAsync (stream, null);
@@ -55,9 +91,23 @@ namespace System.Net.Http
                        if (stream == null)
                                throw new ArgumentNullException ("stream");
 
+                       if (buffer != null)
+                               return buffer.CopyToAsync (stream);
+
                        return SerializeToStreamAsync (stream, context);
                }
 
+               protected async virtual Task<Stream> CreateContentReadStreamAsync ()
+               {
+                       await LoadIntoBufferAsync ().ConfigureAwait (false);
+                       return buffer;
+               }
+               
+               static FixedMemoryStream CreateFixedMemoryStream (long maxBufferSize)
+               {
+                       return new FixedMemoryStream (maxBufferSize);
+               }
+
                public void Dispose ()
                {
                        Dispose (true);
@@ -75,10 +125,10 @@ namespace System.Net.Http
 
                public Task LoadIntoBufferAsync ()
                {
-                       return LoadIntoBufferAsync (0x2000);
+                       return LoadIntoBufferAsync (int.MaxValue);
                }
 
-               public async Task LoadIntoBufferAsync (int maxBufferSize)
+               public async Task LoadIntoBufferAsync (long maxBufferSize)
                {
                        if (disposed)
                                throw new ObjectDisposedException (GetType ().ToString ());
@@ -86,10 +136,24 @@ namespace System.Net.Http
                        if (buffer != null)
                                return;
 
-                       buffer = new MemoryStream ();
+                       buffer = CreateFixedMemoryStream (maxBufferSize);
                        await SerializeToStreamAsync (buffer, null).ConfigureAwait (false);
                        buffer.Seek (0, SeekOrigin.Begin);
                }
+               
+               public async Task<Stream> ReadAsStreamAsync ()
+               {
+                       if (disposed)
+                               throw new ObjectDisposedException (GetType ().ToString ());
+
+                       if (buffer != null)
+                               return new MemoryStream (buffer.GetBuffer (), 0, (int)buffer.Length, false);
+
+                       if (stream == null)
+                               stream = await CreateContentReadStreamAsync ().ConfigureAwait (false);
+
+                       return stream;
+               }
 
                public async Task<byte[]> ReadAsByteArrayAsync ()
                {
@@ -99,21 +163,50 @@ namespace System.Net.Http
 
                public async Task<string> ReadAsStringAsync ()
                {
-                       await LoadIntoBufferAsync ();
+                       await LoadIntoBufferAsync ().ConfigureAwait (false);
                        if (buffer.Length == 0)
                                return string.Empty;
 
+                       var buf = buffer.GetBuffer ();
+                       var buf_length = (int) buffer.Length;
+                       int preambleLength = 0;
                        Encoding encoding;
+
                        if (headers != null && headers.ContentType != null && headers.ContentType.CharSet != null) {
                                encoding = Encoding.GetEncoding (headers.ContentType.CharSet);
+                               preambleLength = StartsWith (buf, buf_length, encoding.GetPreamble ());
                        } else {
-                               encoding = Encoding.UTF8;
+                               encoding = GetEncodingFromBuffer (buf, buf_length, ref preambleLength) ?? Encoding.UTF8;
+                       }
+
+                       return encoding.GetString (buf, preambleLength, buf_length - preambleLength);
+               }
+
+               static Encoding GetEncodingFromBuffer (byte[] buffer, int length, ref int preambleLength)
+               {
+                       var encodings_with_preamble = new [] { Encoding.UTF8, Encoding.UTF32, Encoding.Unicode };
+                       foreach (var enc in encodings_with_preamble) {
+                               if ((preambleLength = StartsWith (buffer, length, enc.GetPreamble ())) != 0)
+                                       return enc;
+                       }
+
+                       return null;
+               }
+
+               static int StartsWith (byte[] array, int length, byte[] value)
+               {
+                       if (length < value.Length)
+                               return 0;
+
+                       for (int i = 0; i < value.Length; ++i) {
+                               if (array [i] != value [i])
+                                       return 0;
                        }
 
-                       return encoding.GetString (buffer.GetBuffer (), 0, (int) buffer.Length);
+                       return value.Length;
                }
 
-               protected abstract Task SerializeToStreamAsync (Stream stream, TransportContext context);
+               protected internal abstract Task SerializeToStreamAsync (Stream stream, TransportContext context);
                protected internal abstract bool TryComputeLength (out long length);
        }
 }