diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs index e1d6a138b10..6d8dfcdb551 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs @@ -91,7 +91,7 @@ public class AndroidClientHandler : HttpClientHandler bool decompress_here; URL java_url; - URLConnection java_connection; + HttpURLConnection java_connection; /// /// @@ -189,33 +189,30 @@ protected void AssertSelf () if (!request.RequestUri.IsAbsoluteUri) throw new ArgumentException ("Must represent an absolute URI", "request"); - /*using (*/java_url = new URL (request.RequestUri.ToString ());/*) {*/ - /*using (*/java_connection = java_url.OpenConnection ();/*) {*/ - HttpURLConnection httpConnection = SetupRequestInternal (request, java_connection); + java_url = new URL (request.RequestUri.ToString ()); + java_connection = java_url.OpenConnection () as HttpURLConnection; + HttpURLConnection httpConnection = await SetupRequestInternal (request, java_connection).ConfigureAwait (false); return await ProcessRequest (request, httpConnection, cancellationToken); - /*} - }*/ } - async Task ProcessRequest (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken) + Task ProcessRequest (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested (); httpConnection.InstanceFollowRedirects = AllowAutoRedirect; RequestedAuthentication = null; ProxyAuthenticationRequested = false; - return await Task.Factory.StartNew (() => DoProcessRequest (request, httpConnection, cancellationToken), cancellationToken).ConfigureAwait (false); + return DoProcessRequest (request, httpConnection, cancellationToken); } - HttpResponseMessage DoProcessRequest (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken) + async Task DoProcessRequest (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken) { if (Logger.LogNet) Logger.Log (LogLevel.Info, LOG_APP, $"{this}.DoProcessRequest ()"); - httpConnection.RequestMethod = request.Method.ToString (); try { if (Logger.LogNet) Logger.Log (LogLevel.Info, LOG_APP, $" connecting"); - httpConnection.Connect (); + await httpConnection.ConnectAsync ().ConfigureAwait (false); if (Logger.LogNet) Logger.Log (LogLevel.Info, LOG_APP, $" connected"); } catch (Java.Net.ConnectException ex) { @@ -372,9 +369,9 @@ void CopyHeaders (HttpURLConnection httpConnection, HttpResponseMessage response /// /// Request data /// Pre-configured connection instance - protected virtual void SetupRequest (HttpRequestMessage request, HttpURLConnection conn) + protected virtual Task SetupRequest (HttpRequestMessage request, HttpURLConnection conn) { - AssertSelf (); + return Task.Factory.StartNew (AssertSelf); } /// @@ -433,20 +430,20 @@ void AppendEncoding (string encoding, ref List list) list.Add (encoding); } - HttpURLConnection SetupRequestInternal (HttpRequestMessage request, URLConnection conn) + async Task SetupRequestInternal (HttpRequestMessage request, HttpURLConnection conn) { if (conn == null) throw new ArgumentNullException (nameof (conn)); - var httpConnection = conn.JavaCast (); + var httpConnection = conn; if (httpConnection == null) throw new InvalidOperationException ($"Unsupported URL scheme {conn.URL.Protocol}"); - + httpConnection.RequestMethod = request.Method.ToString (); // SSL context must be set up as soon as possible, before adding any content or // headers. Otherwise Java won't use the socket factory - SetupSSL (httpConnection as HttpsURLConnection); + await SetupSSL (httpConnection as HttpsURLConnection); if (request.Content != null) - AddHeaders (httpConnection, request.Content.Headers); - AddHeaders (httpConnection, request.Headers); + await AddHeaders (httpConnection, request.Content.Headers); + await AddHeaders (httpConnection, request.Headers); List accept_encoding = null; @@ -475,15 +472,16 @@ HttpURLConnection SetupRequestInternal (HttpRequestMessage request, URLConnectio httpConnection.SetRequestProperty ("Cookie", cookieHeaderValue); } - HandlePreAuthentication (httpConnection); - SetupRequest (request, httpConnection); - SetupRequestBody (httpConnection, request); + await HandlePreAuthentication (httpConnection); + await SetupRequest (request, httpConnection); + await SetupRequestBody (httpConnection, request); return httpConnection; } - void SetupSSL (HttpsURLConnection httpsConnection) + Task SetupSSL (HttpsURLConnection httpsConnection) { + return Task.Factory.StartNew (() => { if (httpsConnection == null) return; @@ -516,10 +514,12 @@ void SetupSSL (HttpsURLConnection httpsConnection) SSLContext context = SSLContext.GetInstance ("TLS"); context.Init (kmf?.GetKeyManagers (), tmf.GetTrustManagers (), null); httpsConnection.SSLSocketFactory = context.SocketFactory; + }); } - void HandlePreAuthentication (HttpURLConnection httpConnection) + Task HandlePreAuthentication (HttpURLConnection httpConnection) { + return Task.Factory.StartNew (() => { AuthenticationData data = PreAuthenticationData; if (!PreAuthenticate || data == null) return; @@ -548,37 +548,55 @@ void HandlePreAuthentication (HttpURLConnection httpConnection) if (Logger.LogNet) Logger.Log (LogLevel.Info, LOG_APP, $"Authentication header '{data.UseProxyAuthentication ? "Proxy-Authorization" : "Authorization"}' will be set to '{authorization.Message}'"); httpConnection.SetRequestProperty (data.UseProxyAuthentication ? "Proxy-Authorization" : "Authorization", authorization.Message); + }); } - void AddHeaders (HttpURLConnection conn, HttpHeaders headers) + Task AddHeaders (HttpURLConnection conn, HttpHeaders headers) { + return Task.Factory.StartNew (() => { if (headers == null) return; foreach (KeyValuePair> header in headers) { conn.SetRequestProperty (header.Key, header.Value != null ? String.Join (",", header.Value) : String.Empty); } + }); } - void SetupRequestBody (HttpURLConnection conn, HttpRequestMessage request) + async Task SetupRequestBody (HttpURLConnection httpConnection, HttpRequestMessage request) { if (request.Content == null) { + httpConnection.SetChunkedStreamingMode (0); // Pilfered from System.Net.Http.HttpClientHandler:SendAync if (HttpMethod.Post.Equals (request.Method) || HttpMethod.Put.Equals (request.Method) || HttpMethod.Delete.Equals (request.Method)) { // Explicitly set this to make sure we're sending a "Content-Length: 0" header. // This fixes the issue that's been reported on the forums: // http://forums.xamarin.com/discussion/17770/length-required-error-in-http-post-since-latest-release - conn.SetRequestProperty ("Content-Length", "0"); + httpConnection.SetRequestProperty ("Content-Length", "0"); } return; } - conn.DoOutput = true; - long? contentLength = request.Content.Headers.ContentLength; - if (contentLength != null) - conn.SetFixedLengthStreamingMode ((int)contentLength); + httpConnection.DoOutput = true; + + var bytes = await request.Content.ReadAsByteArrayAsync ().ConfigureAwait (false); + + int contentLength = bytes.Length; + httpConnection.SetRequestProperty ("Content-Length", contentLength.ToString()); + httpConnection.SetFixedLengthStreamingMode (contentLength); + + string contentType; + if (request.Content.Headers.ContentType != null) + contentType = String.Join (" ", request.Content.Headers.GetValues ("Content-Type")); else - conn.SetChunkedStreamingMode (0); + contentType = "text/plain"; + httpConnection.SetRequestProperty ("Content-Type", contentType); + + await Task.Factory.StartNew (() => { + httpConnection.OutputStream.Write (bytes, 0, contentLength); + httpConnection.OutputStream.Flush (); + httpConnection.OutputStream.Close (); + }); } } }