Merge pull request #1659 from alexanderkyte/stringbuilder-referencesource
[mono.git] / mcs / class / System / System.Net / HttpWebRequest.cs
index e21e5ecc4d45cc1f1cfe075f430728856d14611d..7ccbcc2f34e104f80fadfedc6a3dbe262d13e073 100644 (file)
@@ -56,6 +56,7 @@ namespace System.Net
                bool allowBuffering = true;
                X509CertificateCollection certificates;
                string connectionGroup;
+               bool haveContentLength;
                long contentLength = -1;
                HttpContinueDelegate continueDelegate;
                CookieContainer cookieContainer;
@@ -199,12 +200,13 @@ namespace System.Net
                        set { allowBuffering = value; }
                }
                
-#if NET_4_5
                public virtual bool AllowReadStreamBuffering {
-                       get { return allowBuffering; }
-                       set { allowBuffering = value; }
+                       get { return false; }
+                       set {
+                               if (value)
+                                       throw new InvalidOperationException ();
+                       }
                }
-#endif
 
                static Exception GetMustImplement ()
                {
@@ -277,6 +279,7 @@ namespace System.Net
                                        throw new ArgumentOutOfRangeException ("value", "Content-Length must be >= 0");
                                        
                                contentLength = value;
+                               haveContentLength = true;
                        }
                }
                
@@ -302,9 +305,7 @@ namespace System.Net
                        set { continueDelegate = value; }
                }
                
-#if NET_4_5
                virtual
-#endif
                public CookieContainer CookieContainer {
                        get { return cookieContainer; }
                        set { cookieContainer = value; }
@@ -314,7 +315,6 @@ namespace System.Net
                        get { return credentials; }
                        set { credentials = value; }
                }
-#if NET_4_0
                public DateTime Date {
                        get {
                                string date = webHeaders ["Date"];
@@ -329,7 +329,6 @@ namespace System.Net
                                        webHeaders.RemoveAndAdd ("Date", value.ToUniversalTime ().ToString ("r", CultureInfo.InvariantCulture));
                        }
                }
-#endif
 
 #if !NET_2_1
                [MonoTODO]
@@ -375,9 +374,7 @@ namespace System.Net
                        }
                }
                
-#if NET_4_5
                virtual
-#endif
                public bool HaveResponse {
                        get { return haveResponse; }
                }
@@ -395,11 +392,7 @@ namespace System.Net
                        }
                }
                
-#if NET_4_0
                public
-#else
-               internal
-#endif
                string Host {
                        get {
                                if (host == null)
@@ -501,13 +494,11 @@ namespace System.Net
                        }
                }
                
-#if NET_4_5
                [MonoTODO]
                public int ContinueTimeout {
                        get { throw new NotImplementedException (); }
                        set { throw new NotImplementedException (); }
                }
-#endif
                
                public string MediaType {
                        get { return mediaType; }
@@ -592,7 +583,6 @@ namespace System.Net
                internal ServicePoint ServicePointNoLock {
                        get { return servicePoint; }
                }
-#if NET_4_0
                public virtual bool SupportsCookieContainer { 
                        get {
                                // The managed implementation supports the cookie container
@@ -600,7 +590,6 @@ namespace System.Net
                                return true;
                        }
                }
-#endif
                public override int Timeout { 
                        get { return timeout; }
                        set {
@@ -702,31 +691,19 @@ namespace System.Net
                {
                        AddRange (rangeSpecifier, (long) from, (long) to);
                }
-#if NET_4_0
                public
-#else
-               internal
-#endif
                void AddRange (long range)
                {
                        AddRange ("bytes", (long) range);
                }
 
-#if NET_4_0
                public
-#else
-               internal
-#endif
                void AddRange (long from, long to)
                {
                        AddRange ("bytes", from, to);
                }
 
-#if NET_4_0
                public
-#else
-               internal
-#endif
                void AddRange (string rangeSpecifier, long range)
                {
                        if (rangeSpecifier == null)
@@ -752,11 +729,7 @@ namespace System.Net
                        webHeaders.RemoveAndAdd ("Range", r);
                }
 
-#if NET_4_0
                public
-#else
-               internal
-#endif
                void AddRange (string rangeSpecifier, long from, long to)
                {
                        if (rangeSpecifier == null)
@@ -863,29 +836,24 @@ namespace System.Net
                        return EndGetRequestStream (asyncResult);
                }
 
-               WebAsyncResult CheckIfForceWrite (AsyncCallback callback, object state)
+               bool CheckIfForceWrite (SimpleAsyncResult result)
                {
                        if (writeStream == null || writeStream.RequestWritten || !InternalAllowBuffering)
-                               return null;
-#if NET_4_0
+                               return false;
                        if (contentLength < 0 && writeStream.CanWrite == true && writeStream.WriteBufferLength < 0)
-                               return null;
+                               return false;
 
                        if (contentLength < 0 && writeStream.WriteBufferLength >= 0)
                                InternalContentLength = writeStream.WriteBufferLength;
-#else
-                       if (contentLength < 0 && writeStream.CanWrite == true)
-                               return null;
-#endif
 
                        // This will write the POST/PUT if the write stream already has the expected
                        // amount of bytes in it (ContentLength) (bug #77753) or if the write stream
                        // contains data and it has been closed already (xamarin bug #1512).
 
                        if (writeStream.WriteBufferLength == contentLength || (contentLength == -1 && writeStream.CanWrite == false))
-                               return writeStream.WriteRequestAsync (callback, state);
+                               return writeStream.WriteRequestAsync (result);
 
-                       return null;
+                       return false;
                }
 
                public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)
@@ -912,59 +880,41 @@ namespace System.Net
                        WebAsyncResult aread = asyncRead;
                        initialMethod = method;
 
-                       aread.InnerAsyncResult = CheckIfForceWrite (GetResponseAsyncCB, aread);
-                       if (aread.InnerAsyncResult == null)
-                               GetResponseAsyncCB2 (aread);
-                       else
-                               Monitor.Exit (locker);
-                       return aread;
-               }
-
-               void GetResponseAsyncCB (IAsyncResult ar)
-               {
-                       var result = (WebAsyncResult)ar;
-                       var innerResult = (WebAsyncResult)result.InnerAsyncResult;
-                       result.InnerAsyncResult = null;
-
-                       if (innerResult != null && innerResult.GotException) {
-                               asyncRead.SetCompleted (true, innerResult.Exception);
-                               asyncRead.DoCallback ();
-                               return;
-                       }
-
-                       Monitor.Enter (locker);
-                       GetResponseAsyncCB2 ((WebAsyncResult)ar);
-               }
+                       SimpleAsyncResult.RunWithLock (locker, CheckIfForceWrite, inner => {
+                               var synch = inner.CompletedSynchronously;
 
-               void GetResponseAsyncCB2 (WebAsyncResult aread)
-               {
-                       if (haveResponse) {
-                               Exception saved = saved_exc;
-                               if (webResponse != null) {
-                                       Monitor.Exit (locker);
-                                       if (saved == null) {
-                                               aread.SetCompleted (true, webResponse);
-                                       } else {
-                                               aread.SetCompleted (true, saved);
-                                       }
-                                       aread.DoCallback ();
-                                       return;
-                               } else if (saved != null) {
-                                       Monitor.Exit (locker);
-                                       aread.SetCompleted (true, saved);
+                               if (inner.GotException) {
+                                       aread.SetCompleted (synch, inner.Exception);
                                        aread.DoCallback ();
                                        return;
                                }
-                       }
 
-                       if (!requestSent) {
-                               requestSent = true;
-                               redirects = 0;
-                               servicePoint = GetServicePoint ();
-                               abortHandler = servicePoint.SendRequest (this, connectionGroup);
-                       }
+                               if (haveResponse) {
+                                       Exception saved = saved_exc;
+                                       if (webResponse != null) {
+                                               if (saved == null) {
+                                                       aread.SetCompleted (synch, webResponse);
+                                               } else {
+                                                       aread.SetCompleted (synch, saved);
+                                               }
+                                               aread.DoCallback ();
+                                               return;
+                                       } else if (saved != null) {
+                                               aread.SetCompleted (synch, saved);
+                                               aread.DoCallback ();
+                                               return;
+                                       }
+                               }
 
-                       Monitor.Exit (locker);
+                               if (!requestSent) {
+                                       requestSent = true;
+                                       redirects = 0;
+                                       servicePoint = GetServicePoint ();
+                                       abortHandler = servicePoint.SendRequest (this, connectionGroup);
+                               }
+                       });
+
+                       return aread;
                }
 
                public override WebResponse EndGetResponse (IAsyncResult asyncResult)
@@ -987,13 +937,11 @@ namespace System.Net
                        return result.Response;
                }
                
-#if NET_3_5
                public Stream EndGetRequestStream (IAsyncResult asyncResult, out TransportContext transportContext)
                {
                        transportContext = null;
                        return EndGetRequestStream (asyncResult);
                }
-#endif
 
                public override WebResponse GetResponse()
                {
@@ -1117,7 +1065,7 @@ namespace System.Net
                        sendChunked = false;
                }
                
-               bool Redirect (WebAsyncResult result, HttpStatusCode code)
+               bool Redirect (WebAsyncResult result, HttpStatusCode code, WebResponse response)
                {
                        redirects++;
                        Exception e = null;
@@ -1147,12 +1095,15 @@ namespace System.Net
                                break;
                        }
 
+                       if (method != "GET" && !InternalAllowBuffering && (writeStream.WriteBufferLength > 0 || contentLength > 0))
+                               e = new WebException ("The request requires buffering data to succeed.", null, WebExceptionStatus.ProtocolError, webResponse);
+
                        if (e != null)
                                throw e;
 
-                       contentLength = -1;
-                       //bodyBufferLength = 0;
-                       //bodyBuffer = null;
+                       if (AllowWriteStreamBuffering)
+                               contentLength = -1;
+
                        uriString = webResponse.Headers ["Location"];
 
                        if (uriString == null)
@@ -1182,12 +1133,15 @@ namespace System.Net
                        } else if (contentLength != -1) {
                                if (auth_state.NtlmAuthState == NtlmAuthState.Challenge || proxy_auth_state.NtlmAuthState == NtlmAuthState.Challenge) {
                                        // We don't send any body with the NTLM Challenge request.
-                                       webHeaders.SetInternal ("Content-Length", "0");
+                                       if (haveContentLength || gotRequestStream || contentLength > 0)
+                                               webHeaders.SetInternal ("Content-Length", "0");
+                                       else
+                                               webHeaders.RemoveInternal ("Content-Length");
                                } else {
                                        if (contentLength > 0)
                                                continue100 = true;
 
-                                       if (gotRequestStream || contentLength > 0)
+                                       if (haveContentLength || gotRequestStream || contentLength > 0)
                                                webHeaders.SetInternal ("Content-Length", contentLength.ToString ());
                                }
                                webHeaders.RemoveInternal ("Transfer-Encoding");
@@ -1317,69 +1271,58 @@ namespace System.Net
                                writeStream.SendChunked = false;
                        }
 
-                       try {
-                               var result = writeStream.SetHeadersAsync (false, SetWriteStreamCB, null);
-                               if (result == null)
-                                       SetWriteStreamCB (null);
-                       } catch (Exception exc) {
-                               SetWriteStreamErrorCB (exc);
-                       }
-               }
-
-               void SetWriteStreamErrorCB (Exception exc)
-               {
-                       WebException wexc = exc as WebException;
-                       if (wexc != null)
-                               SetWriteStreamError (wexc.Status, wexc);
-                       else
-                               SetWriteStreamError (WebExceptionStatus.SendFailure, exc);
-               }
+                       writeStream.SetHeadersAsync (false, result => {
+                               if (result.GotException) {
+                                       SetWriteStreamError (result.Exception);
+                                       return;
+                               }
 
-               void SetWriteStreamCB (IAsyncResult ar)
-               {
-                       WebAsyncResult result = ar as WebAsyncResult;
+                               haveRequest = true;
 
-                       if (result != null && result.Exception != null) {
-                               SetWriteStreamErrorCB (result.Exception);
-                               return;
-                       }
-               
-                       haveRequest = true;
+                               SetWriteStreamInner (inner => {
+                                       if (inner.GotException) {
+                                               SetWriteStreamError (inner.Exception);
+                                               return;
+                                       }
 
-                       WebAsyncResult writeRequestResult = null;
+                                       if (asyncWrite != null) {
+                                               asyncWrite.SetCompleted (inner.CompletedSynchronously, writeStream);
+                                               asyncWrite.DoCallback ();
+                                               asyncWrite = null;
+                                       }
+                               });
+                       });
+               }
 
-                       if (bodyBuffer != null) {
-                               // The body has been written and buffered. The request "user"
-                               // won't write it again, so we must do it.
-                               if (auth_state.NtlmAuthState != NtlmAuthState.Challenge && proxy_auth_state.NtlmAuthState != NtlmAuthState.Challenge) {
-                                       // FIXME: this is a blocking call on the thread pool that could lead to thread pool exhaustion
-                                       writeStream.Write (bodyBuffer, 0, bodyBufferLength);
-                                       bodyBuffer = null;
-                                       writeStream.Close ();
+               void SetWriteStreamInner (SimpleAsyncCallback callback)
+               {
+                       SimpleAsyncResult.Run (result => {
+                               if (bodyBuffer != null) {
+                                       // The body has been written and buffered. The request "user"
+                                       // won't write it again, so we must do it.
+                                       if (auth_state.NtlmAuthState != NtlmAuthState.Challenge && proxy_auth_state.NtlmAuthState != NtlmAuthState.Challenge) {
+                                               // FIXME: this is a blocking call on the thread pool that could lead to thread pool exhaustion
+                                               writeStream.Write (bodyBuffer, 0, bodyBufferLength);
+                                               bodyBuffer = null;
+                                               writeStream.Close ();
+                                       }
+                               } else if (method != "HEAD" && method != "GET" && method != "MKCOL" && method != "CONNECT" &&
+                                         method != "TRACE") {
+                                       if (getResponseCalled && !writeStream.RequestWritten)
+                                               return writeStream.WriteRequestAsync (result);
                                }
-                       } else if (method != "HEAD" && method != "GET" && method != "MKCOL" && method != "CONNECT" &&
-                                       method != "TRACE") {
-                               if (getResponseCalled && !writeStream.RequestWritten)
-                                       writeRequestResult = writeStream.WriteRequestAsync (SetWriteStreamCB2, null);
-                       }
 
-                       if (writeRequestResult == null)
-                               SetWriteStreamCB2 (null);
+                               return false;
+                       }, callback);
                }
 
-               void SetWriteStreamCB2 (IAsyncResult ar)
+               void SetWriteStreamError (Exception exc)
                {
-                       var result = (WebAsyncResult)ar;
-                       if (result != null && result.GotException) {
-                               SetWriteStreamErrorCB (result.Exception);
-                               return;
-                       }
-
-                       if (asyncWrite != null) {
-                               asyncWrite.SetCompleted (false, writeStream);
-                               asyncWrite.DoCallback ();
-                               asyncWrite = null;
-                       }
+                       WebException wexc = exc as WebException;
+                       if (wexc != null)
+                               SetWriteStreamError (wexc.Status, wexc);
+                       else
+                               SetWriteStreamError (WebExceptionStatus.SendFailure, exc);
                }
 
                internal void SetResponseError (WebExceptionStatus status, Exception e, string where)
@@ -1525,6 +1468,11 @@ namespace System.Net
                                                r.SetCompleted (false, webResponse);
                                                r.DoCallback ();
                                        } else {
+                                               if (sendChunked) {
+                                                       sendChunked = false;
+                                                       webHeaders.RemoveInternal ("Transfer-Encoding");
+                                               }
+
                                                if (webResponse != null) {
                                                        if (HandleNtlmAuth (r))
                                                                return;
@@ -1659,11 +1607,7 @@ namespace System.Net
                                        if (!usedPreAuth && CheckAuthorization (webResponse, code)) {
                                                // Keep the written body, so it can be rewritten in the retry
                                                if (InternalAllowBuffering) {
-                                                       // NTLM: This is to avoid sending data in the 'challenge' request
-                                                       // We save it in the first request (first 401), don't send anything
-                                                       // in the challenge request and send it in the response request along
-                                                       // with the buffers kept form the first request.
-                                                       if (auth_state.NtlmAuthState == NtlmAuthState.Challenge || proxy_auth_state.NtlmAuthState == NtlmAuthState.Challenge) {
+                                                       if (writeStream.WriteBufferLength > 0) {
                                                                bodyBuffer = writeStream.WriteBuffer;
                                                                bodyBufferLength = writeStream.WriteBufferLength;
                                                        }
@@ -1710,7 +1654,7 @@ namespace System.Net
                                bool b = false;
                                int c = (int) code;
                                if (allowAutoRedirect && c >= 300) {
-                                       b = Redirect (result, code);
+                                       b = Redirect (result, code, webResponse);
                                        if (InternalAllowBuffering && writeStream.WriteBufferLength > 0) {
                                                bodyBuffer = writeStream.WriteBuffer;
                                                bodyBufferLength = writeStream.WriteBufferLength;