Merge pull request #829 from symform/httpwebreq-async-ssl
authorSebastien Pouliot <sebastien.pouliot@gmail.com>
Fri, 7 Feb 2014 21:48:05 +0000 (16:48 -0500)
committerSebastien Pouliot <sebastien.pouliot@gmail.com>
Fri, 7 Feb 2014 21:48:05 +0000 (16:48 -0500)
Convert blocking operations in HttpWebRequest and SslClientStream to non-blocking

1  2 
mcs/class/System/System.Net/HttpWebRequest.cs

index 1cdffff53304462ef786183bc0e62467ed06c10b,6aee5af52bd3bda083667b07cc29d699d178f597..af664e800d4240cbfe068c3f7c977de9a2e1c066
@@@ -36,7 -36,6 +36,7 @@@ using System.Collections
  using System.Configuration;
  using System.Globalization;
  using System.IO;
 +using System.Net;
  using System.Net.Cache;
  using System.Net.Sockets;
  using System.Runtime.Remoting.Messaging;
@@@ -951,14 -950,6 +951,14 @@@ namespace System.Ne
  
                        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()
                {
                        redirects++;
                        Exception e = null;
                        string uriString = null;
 -
                        switch (code) {
                        case HttpStatusCode.Ambiguous: // 300
                                e = new WebException ("Ambiguous redirect.");
                                break;
                        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;
 -                              */
 -
 -                              contentLength = -1;
 -                              bodyBufferLength = 0;
 -                              bodyBuffer = null;
 -                              if (code != HttpStatusCode.TemporaryRedirect)
 +                              if (method == "POST")
                                        method = "GET";
 -                              uriString = webResponse.Headers ["Location"];
 +                              break;
 +                      case HttpStatusCode.TemporaryRedirect: // 307
                                break;
                        case HttpStatusCode.SeeOther: //303
                                method = "GET";
 -                              uriString = webResponse.Headers ["Location"];
                                break;
                        case HttpStatusCode.NotModified: // 304
                                return false;
                        if (e != null)
                                throw e;
  
 +                      //contentLength = -1;
 +                      //bodyBufferLength = 0;
 +                      //bodyBuffer = null;
 +                      uriString = webResponse.Headers ["Location"];
 +
                        if (uriString == null)
                                throw new WebException ("No Location header found for " + (int) code,
                                                        WebExceptionStatus.ProtocolError);
                        }
                }
  
-               internal void SendRequestHeaders (bool propagate_error)
+               internal byte[] GetRequestHeaders ()
                {
                        StringBuilder req = new StringBuilder ();
                        string query;
                                                                actualVersion.Major, actualVersion.Minor);
                        req.Append (GetHeaders ());
                        string reqstr = req.ToString ();
-                       byte [] bytes = Encoding.UTF8.GetBytes (reqstr);
-                       try {
-                               writeStream.SetHeaders (bytes);
-                       } catch (WebException wexc) {
-                               SetWriteStreamError (wexc.Status, wexc);
-                               if (propagate_error)
-                                       throw;
-                       } catch (Exception exc) {
-                               SetWriteStreamError (WebExceptionStatus.SendFailure, exc);
-                               if (propagate_error)
-                                       throw;
-                       }
+                       return Encoding.UTF8.GetBytes (reqstr);
                }
  
                internal void SetWriteStream (WebConnectionStream stream)
                                writeStream.SendChunked = false;
                        }
  
-                       SendRequestHeaders (false);
+                       byte[] requestHeaders = GetRequestHeaders ();
+                       WebAsyncResult result = new WebAsyncResult (new AsyncCallback (SetWriteStreamCB), null);
+                       writeStream.SetHeadersAsync (requestHeaders, result);
+               }
+               void SetWriteStreamCB(IAsyncResult ar)
+               {
+                       WebAsyncResult result = ar as WebAsyncResult;
  
+                       if (result.Exception != null) {
+                               WebException wexc = result.Exception as WebException;
+                               if (wexc != null) {
+                                       SetWriteStreamError (wexc.Status, wexc);
+                                       return;
+                               }
+                               SetWriteStreamError (WebExceptionStatus.SendFailure, result.Exception);
+                               return;
+                       }
+               
                        haveRequest = true;
-                       
                        if (bodyBuffer != null) {
                                // The body has been written and buffered. The request "user"
                                // won't write it again, so we must do it.
                                if (ntlm_auth_state != 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)
+                                       // FIXME: this is a blocking call on the thread pool that could lead to thread pool exhaustion
                                        writeStream.WriteRequest ();
                        }
  
                        if (asyncWrite != null) {
-                               asyncWrite.SetCompleted (false, stream);
+                               asyncWrite.SetCompleted (false, writeStream);
                                asyncWrite.DoCallback ();
                                asyncWrite = null;
                        }