New test.
[mono.git] / mcs / class / System / System.Net / HttpWebRequest.cs
index af18f9a6154d64d0b81b97a12f96ec5897ee8cdc..9783525f71e150c29619d2636226f30f5cc48630 100644 (file)
@@ -89,10 +89,13 @@ namespace System.Net
                bool authCompleted;
                byte[] bodyBuffer;
                int bodyBufferLength;
+               bool getResponseCalled;
+               Exception saved_exc;
+               object locker = new object ();
 #if NET_1_1
                int maxResponseHeadersLength;
                static int defaultMaxResponseHeadersLength;
-               int readWriteTimeout;
+               int readWriteTimeout = 300000; // ms
                
                // Constructors
                static HttpWebRequest ()
@@ -226,7 +229,6 @@ namespace System.Net
                public override string ContentType { 
                        get { return webHeaders ["Content-Type"]; }
                        set {
-                               CheckRequestStarted ();
                                if (value == null || value.Trim().Length == 0) {
                                        webHeaders.RemoveInternal ("Content-Type");
                                        return;
@@ -339,12 +341,22 @@ namespace System.Net
                        set { defaultMaxResponseHeadersLength = value; }
                }
 
-               [MonoTODO ("Use this")]
-               public int ReadWriteTimeout {
+               public
+#else
+               internal
+#endif
+               int ReadWriteTimeout {
                        get { return readWriteTimeout; }
-                       set { readWriteTimeout = value; }
+                       set {
+                               if (requestSent)
+                                       throw new InvalidOperationException ("The request has already been sent.");
+
+                               if (value < -1)
+                                       throw new ArgumentOutOfRangeException ("value", "Must be >= -1");
+
+                               readWriteTimeout = value;
+                       }
                }
-#endif
                
                public string MediaType {
                        get { return mediaType; }
@@ -462,11 +474,12 @@ namespace System.Net
                }
 
 #if NET_1_1
+               bool unsafe_auth_blah;
                [MonoTODO]
                public bool UnsafeAuthenticatedConnectionSharing
                {
-                       get { throw new NotImplementedException (); }
-                       set { throw new NotImplementedException (); }
+                       get { return unsafe_auth_blah; }
+                       set { unsafe_auth_blah = value; }
                }
 #endif
 
@@ -491,10 +504,7 @@ namespace System.Net
                
                internal ServicePoint GetServicePoint ()
                {
-                       if (!hostChanged && servicePoint != null)
-                               return servicePoint;
-
-                       lock (this) {
+                       lock (locker) {
                                if (hostChanged || servicePoint == null) {
                                        servicePoint = ServicePointManager.FindServicePoint (actualUri, proxy);
                                        hostChanged = false;
@@ -525,7 +535,7 @@ namespace System.Net
                                value += ",";
                        else
                                throw new InvalidOperationException ("rangeSpecifier");
-                       webHeaders.SetInternal ("Range", value + range + "-");  
+                       webHeaders.RemoveAndAdd ("Range", value + range + "-"); 
                }
                
                public void AddRange (string rangeSpecifier, int from, int to)
@@ -541,13 +551,14 @@ namespace System.Net
                                value += ",";
                        else
                                throw new InvalidOperationException ("rangeSpecifier");
-                       webHeaders.SetInternal ("Range", value + from + "-" + to);      
+                       webHeaders.RemoveAndAdd ("Range", value + from + "-" + to);     
                }
-               
+#if !NET_2_0
                public override int GetHashCode ()
                {
                        return base.GetHashCode ();
                }
+#endif
                
                void CommonChecks (bool putpost)
                {
@@ -573,7 +584,7 @@ namespace System.Net
 
                        CommonChecks (send);
                        
-                       lock (this)
+                       lock (locker)
                        {
                                if (asyncWrite != null) {
                                        throw new InvalidOperationException ("Cannot re-call start of asynchronous " +
@@ -594,6 +605,7 @@ namespace System.Net
                                WebAsyncResult result = asyncWrite;
                                if (!requestSent) {
                                        requestSent = true;
+                                       redirects = 0;
                                        servicePoint = GetServicePoint ();
                                        abortHandler = servicePoint.SendRequest (this, connectionGroup);
                                }
@@ -631,7 +643,18 @@ namespace System.Net
 
                        return EndGetRequestStream (asyncResult);
                }
-               
+
+               void CheckIfForceWrite ()
+               {
+                       if (writeStream == null || contentLength < 0 || !InternalAllowBuffering)
+                               return;
+
+                       // This will write the POST/PUT if the write stream already has the expected
+                       // amount of bytes in it (ContentLength) (bug #77753).
+                       if (writeStream.WriteBufferLength == contentLength)
+                               writeStream.WriteRequest ();
+               }
+
                public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state)
                {
                        bool send = (method == "PUT" || method == "POST");
@@ -642,18 +665,25 @@ namespace System.Net
 
                        CommonChecks (send);
                        Monitor.Enter (this);
+                       getResponseCalled = true;
                        if (asyncRead != null && !haveResponse) {
                                Monitor.Exit (this);
                                throw new InvalidOperationException ("Cannot re-call start of asynchronous " +
                                                        "method while a previous call is still in progress.");
                        }
 
+                       CheckIfForceWrite ();
                        asyncRead = new WebAsyncResult (this, callback, state);
                        initialMethod = method;
                        if (haveResponse) {
                                if (webResponse != null) {
+                                       Exception saved = saved_exc;
                                        Monitor.Exit (this);
-                                       asyncRead.SetCompleted (true, webResponse);
+                                       if (saved == null) {
+                                               asyncRead.SetCompleted (true, webResponse);
+                                       } else {
+                                               asyncRead.SetCompleted (true, saved);
+                                       }
                                        asyncRead.DoCallback ();
                                        return asyncRead;
                                }
@@ -661,6 +691,7 @@ namespace System.Net
                        
                        if (!requestSent) {
                                requestSent = true;
+                               redirects = 0;
                                servicePoint = GetServicePoint ();
                                abortHandler = servicePoint.SendRequest (this, connectionGroup);
                        }
@@ -678,33 +709,19 @@ namespace System.Net
                        if (result == null)
                                throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
 
-                       redirects = 0;
-                       bool redirected = false;
-                       asyncRead = result;
-                       do {
-                               if (redirected) {
-                                       haveResponse = false;
-                                       result.Reset ();
-                                       servicePoint = GetServicePoint ();
-                                       abortHandler = servicePoint.SendRequest (this, connectionGroup);
-                               }
+                       if (!result.WaitUntilComplete (timeout, false)) {
+                               Abort ();
+                               throw new WebException("The request timed out", WebExceptionStatus.Timeout);
+                       }
 
-                               if (!result.WaitUntilComplete (timeout, false)) {
-                                       Abort ();
-                                       throw new WebException("The request timed out", WebExceptionStatus.Timeout);
-                               }
+                       if (result.GotException)
+                               throw result.Exception;
 
-                               redirected = CheckFinalStatus (result);
-                       } while (redirected);
-                       
                        return result.Response;
                }
-               
+
                public override WebResponse GetResponse()
                {
-                       if (haveResponse && webResponse != null)
-                               return webResponse;
-
                        WebAsyncResult result = (WebAsyncResult) BeginGetResponse (null, null);
                        return EndGetResponse (result);
                }
@@ -732,7 +749,7 @@ namespace System.Net
                        if (abortHandler != null) {
                                try {
                                        abortHandler (this, EventArgs.Empty);
-                               } catch {}
+                               } catch (Exception) {}
                                abortHandler = null;
                        }
 
@@ -802,8 +819,10 @@ namespace System.Net
                        case HttpStatusCode.MovedPermanently: // 301
                        case HttpStatusCode.Redirect: // 302
                        case HttpStatusCode.TemporaryRedirect: // 307
+                               /* MS follows the redirect for POST too
                                if (method != "GET" && method != "HEAD") // 10.3
                                        return false;
+                               */
 
                                uriString = webResponse.Headers ["Location"];
                                break;
@@ -846,8 +865,9 @@ namespace System.Net
                string GetHeaders ()
                {
                        bool continue100 = false;
-                       if (gotRequestStream && contentLength != -1) {
-                               continue100 = true;
+                       if (contentLength != -1) {
+                               if (contentLength > 0)
+                                       continue100 = true;
                                webHeaders.SetInternal ("Content-Length", contentLength.ToString ());
                                webHeaders.RemoveInternal ("Transfer-Encoding");
                        } else if (sendChunked) {
@@ -894,12 +914,12 @@ namespace System.Net
                        webHeaders.RemoveInternal ("Proxy-Authorization");
                        webHeaders.RemoveInternal ("Authorization");
                        bool isProxy = (proxy != null && !proxy.IsBypassed (actualUri));
-                       ICredentials creds = (!isProxy) ? credentials : proxy.Credentials;
+                       ICredentials creds = (!isProxy || credentials != null) ? credentials : proxy.Credentials;
                        Authorization auth = AuthenticationManager.PreAuthenticate (this, creds);
                        if (auth == null)
                                return;
 
-                       string authHeader = (isProxy) ? "Proxy-Authorization" : "Authorization";
+                       string authHeader = (isProxy && credentials == null) ? "Proxy-Authorization" : "Authorization";
                        webHeaders [authHeader] = auth.Message;
                        usedPreAuth = true;
                }
@@ -961,7 +981,7 @@ namespace System.Net
                                contentLength = bodyBufferLength;
                                writeStream.SendChunked = false;
                        }
-                       
+
                        SendRequestHeaders ();
 
                        haveRequest = true;
@@ -972,6 +992,9 @@ namespace System.Net
                                writeStream.Write (bodyBuffer, 0, bodyBufferLength);
                                bodyBuffer = null;
                                writeStream.Close ();
+                       } else if (method == "PUT" || method == "POST") {
+                               if (getResponseCalled && !writeStream.RequestWritten)
+                                       writeStream.WriteRequest ();
                        }
 
                        if (asyncWrite != null) {
@@ -981,21 +1004,44 @@ namespace System.Net
                        }
                }
 
-               internal void SetResponseError (WebExceptionStatus status, Exception e)
+               internal void SetResponseError (WebExceptionStatus status, Exception e, string where)
                {
+                       if (aborted)
+                               return;
+                       string msg = String.Format ("Error getting response stream ({0}): {1}", where, status);
                        WebAsyncResult r = asyncRead;
                        if (r == null)
                                r = asyncWrite;
 
                        if (r != null) {
-                               WebException wexc = new WebException ("Error getting response stream: " + status, e, status, null); 
+                               WebException wexc;
+                               if (e is WebException) {
+                                       wexc = (WebException) e;
+                               } else {
+                                       wexc = new WebException (msg, e, status, null); 
+                               }
                                r.SetCompleted (false, wexc);
                                r.DoCallback ();
                                asyncRead = null;
                                asyncWrite = null;
                        }
                }
-               
+
+               void CheckSendError (WebConnectionData data)
+               {
+                       // Got here, but no one called GetResponse
+                       int status = data.StatusCode;
+                       if (status < 400 || status == 401 || status == 407)
+                               return;
+
+                       if (writeStream != null && asyncRead == null && !writeStream.CompleteRequestWritten) {
+                               // The request has not been completely sent and we got here!
+                               // We should probably just close and cause an error in any case,
+                               saved_exc = new WebException (data.StatusDescription, null, WebExceptionStatus.ProtocolError, webResponse); 
+                               webResponse.ReadAll ();
+                       }
+               }
+
                internal void SetResponseData (WebConnectionData data)
                {
                        if (aborted) {
@@ -1003,14 +1049,60 @@ namespace System.Net
                                        data.stream.Close ();
                                return;
                        }
-                       
-                       webResponse = new HttpWebResponse (actualUri, method, data, (cookieContainer != null));
-                       haveResponse = true;
+
+                       WebException wexc = null;
+                       try {
+                               webResponse = new HttpWebResponse (actualUri, method, data, cookieContainer);
+                               haveResponse = true;
+                       } catch (Exception e) {
+                               wexc = new WebException (e.Message, e, WebExceptionStatus.ProtocolError, null); 
+                               if (data.stream != null)
+                                       data.stream.Close ();
+                       }
+
+                       if (wexc == null && (method == "POST" || method == "PUT")) {
+                               lock (locker) {
+                                       CheckSendError (data);
+                                       if (saved_exc != null)
+                                               wexc = (WebException) saved_exc;
+                               }
+                       }
 
                        WebAsyncResult r = asyncRead;
                        if (r != null) {
-                               r.SetCompleted (false, webResponse);
-                               r.DoCallback ();
+                               if (wexc != null) {
+                                       r.SetCompleted (false, wexc);
+                                       r.DoCallback ();
+                                       return;
+                               }
+
+                               bool redirected;
+                               try {
+                                       redirected = CheckFinalStatus (r);
+                                       if (!redirected) {
+                                               r.SetCompleted (false, webResponse);
+                                               r.DoCallback ();
+                                       } else {
+                                               if (webResponse != null) {
+                                                       webResponse.Close ();
+                                                       webResponse = null;
+                                               }
+                                               haveResponse = false;
+                                               webResponse = null;
+                                               r.Reset ();
+                                               servicePoint = GetServicePoint ();
+                                               abortHandler = servicePoint.SendRequest (this, connectionGroup);
+                                       }
+                               } catch (WebException wexc2) {
+                                       r.SetCompleted (false, wexc2);
+                                       r.DoCallback ();
+                                       return;
+                               } catch (Exception ex) {
+                                       wexc = new WebException (ex.Message, ex, WebExceptionStatus.ProtocolError, null); 
+                                       r.SetCompleted (false, wexc);
+                                       r.DoCallback ();
+                                       return;
+                               }
                        }
                }
 
@@ -1051,22 +1143,26 @@ namespace System.Net
                        WebExceptionStatus protoError = WebExceptionStatus.ProtocolError;
                        HttpStatusCode code = 0;
                        if (throwMe == null && webResponse != null) {
-                               code  = webResponse.StatusCode;
+                               code = webResponse.StatusCode;
                                if (!authCompleted && ((code == HttpStatusCode.Unauthorized && credentials != null) ||
-                                                       code == HttpStatusCode.ProxyAuthenticationRequired)) {
+                                    (ProxyQuery && code == HttpStatusCode.ProxyAuthenticationRequired))) {
                                        if (!usedPreAuth && CheckAuthorization (webResponse, code)) {
                                                // Keep the written body, so it can be rewritten in the retry
                                                if (InternalAllowBuffering) {
                                                        bodyBuffer = writeStream.WriteBuffer;
                                                        bodyBufferLength = writeStream.WriteBufferLength;
+                                                       webResponse.Close ();
                                                        return true;
                                                } else if (method != "PUT" && method != "POST") {
+                                                       webResponse.Close ();
                                                        return true;
                                                }
                                                
                                                writeStream.InternalClose ();
                                                writeStream = null;
+                                               webResponse.Close ();
                                                webResponse = null;
+
                                                throw new WebException ("This request requires buffering " +
                                                                        "of data for authentication or " +
                                                                        "redirection to be sucessful.");
@@ -1077,6 +1173,7 @@ namespace System.Net
                                        string err = String.Format ("The remote server returned an error: ({0}) {1}.",
                                                                    (int) code, webResponse.StatusDescription);
                                        throwMe = new WebException (err, null, protoError, webResponse);
+                                       webResponse.ReadAll ();
                                } else if ((int) code == 304 && allowAutoRedirect) {
                                        string err = String.Format ("The remote server returned an error: ({0}) {1}.",
                                                                    (int) code, webResponse.StatusDescription);
@@ -1084,13 +1181,23 @@ namespace System.Net
                                } else if ((int) code >= 300 && allowAutoRedirect && redirects > maxAutoRedirect) {
                                        throwMe = new WebException ("Max. redirections exceeded.", null,
                                                                    protoError, webResponse);
+                                       webResponse.ReadAll ();
                                }
                        }
 
                        if (throwMe == null) {
                                bool b = false;
-                               if (allowAutoRedirect && (int) code >= 300)
+                               int c = (int) code;
+                               if (allowAutoRedirect && c >= 300) {
+                                       if (InternalAllowBuffering && writeStream.WriteBufferLength > 0) {
+                                               bodyBuffer = writeStream.WriteBuffer;
+                                               bodyBufferLength = writeStream.WriteBufferLength;
+                                       }
                                        b = Redirect (result, code);
+                               }
+
+                               if (resp != null && c >= 300 && c != 304)
+                                       resp.ReadAll ();
 
                                return b;
                        }
@@ -1100,8 +1207,7 @@ namespace System.Net
                                writeStream = null;
                        }
 
-                       if (webResponse != null)
-                               webResponse = null;
+                       webResponse = null;
 
                        throw throwMe;
                }