diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs index 6d8dfcdb551..8e98b393f0c 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs @@ -90,9 +90,6 @@ public class AndroidClientHandler : HttpClientHandler // uncompress it any longer, doh. And they don't support 'deflate' so we need to handle it ourselves. bool decompress_here; - URL java_url; - HttpURLConnection java_connection; - /// /// /// Gets or sets the pre authentication data for the request. This property must be set by the application @@ -154,15 +151,6 @@ public bool RequestNeedsAuthorization { protected override void Dispose (bool disposing) { disposed = true; - if (java_connection != null) { - java_connection.Dispose (); - java_connection = null; - } - - if (java_url != null) { - java_url.Dispose (); - java_url = null; - } base.Dispose (disposing); } @@ -180,7 +168,7 @@ protected void AssertSelf () /// Task in which the request is executed /// Request provided by /// Cancellation token. - protected override async Task SendAsync (HttpRequestMessage request, CancellationToken cancellationToken) + protected override Task SendAsync (HttpRequestMessage request, CancellationToken cancellationToken) { AssertSelf (); if (request == null) @@ -189,23 +177,23 @@ protected void AssertSelf () if (!request.RequestUri.IsAbsoluteUri) throw new ArgumentException ("Must represent an absolute URI", "request"); - 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); + URL java_url = new URL (request.RequestUri.ToString ()); + URLConnection java_connection = java_url.OpenConnection (); + HttpURLConnection httpConnection = SetupRequestInternal (request, java_connection); + return ProcessRequest (request, java_url, httpConnection, cancellationToken); } - Task ProcessRequest (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken) + Task ProcessRequest (HttpRequestMessage request, URL javaUrl, HttpURLConnection httpConnection, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested (); httpConnection.InstanceFollowRedirects = AllowAutoRedirect; RequestedAuthentication = null; ProxyAuthenticationRequested = false; - - return DoProcessRequest (request, httpConnection, cancellationToken); + + return DoProcessRequest (request, javaUrl, httpConnection, cancellationToken); } - async Task DoProcessRequest (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken) + async Task DoProcessRequest (HttpRequestMessage request, URL javaUrl, HttpURLConnection httpConnection, CancellationToken cancellationToken) { if (Logger.LogNet) Logger.Log (LogLevel.Info, LOG_APP, $"{this}.DoProcessRequest ()"); @@ -222,12 +210,16 @@ protected void AssertSelf () throw new WebException (ex.Message, ex, WebExceptionStatus.ConnectFailure, null); } + if (httpConnection.DoOutput) { + await request.Content.CopyToAsync (httpConnection.OutputStream).ConfigureAwait (false); + } + var statusCode = (HttpStatusCode)httpConnection.ResponseCode; var connectionUri = new Uri (httpConnection.URL.ToString ()); // If the request was redirected we need to put the new URL in the request request.RequestUri = connectionUri; - var ret = new AndroidHttpResponseMessage { + var ret = new AndroidHttpResponseMessage (javaUrl, httpConnection) { RequestMessage = request, ReasonPhrase = httpConnection.ResponseMessage, StatusCode = statusCode, @@ -369,9 +361,9 @@ void CopyHeaders (HttpURLConnection httpConnection, HttpResponseMessage response /// /// Request data /// Pre-configured connection instance - protected virtual Task SetupRequest (HttpRequestMessage request, HttpURLConnection conn) + protected virtual void SetupRequest (HttpRequestMessage request, HttpURLConnection conn) { - return Task.Factory.StartNew (AssertSelf); + AssertSelf (); } /// @@ -430,20 +422,22 @@ void AppendEncoding (string encoding, ref List list) list.Add (encoding); } - async Task SetupRequestInternal (HttpRequestMessage request, HttpURLConnection conn) + HttpURLConnection SetupRequestInternal (HttpRequestMessage request, URLConnection conn) { if (conn == null) throw new ArgumentNullException (nameof (conn)); - var httpConnection = conn; + var httpConnection = conn.JavaCast (); 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 - await SetupSSL (httpConnection as HttpsURLConnection); + SetupSSL (httpConnection as HttpsURLConnection); if (request.Content != null) - await AddHeaders (httpConnection, request.Content.Headers); - await AddHeaders (httpConnection, request.Headers); + AddHeaders (httpConnection, request.Content.Headers); + AddHeaders (httpConnection, request.Headers); List accept_encoding = null; @@ -472,16 +466,15 @@ void AppendEncoding (string encoding, ref List list) httpConnection.SetRequestProperty ("Cookie", cookieHeaderValue); } - await HandlePreAuthentication (httpConnection); - await SetupRequest (request, httpConnection); - await SetupRequestBody (httpConnection, request); + HandlePreAuthentication (httpConnection); + SetupRequest (request, httpConnection); + SetupRequestBody (httpConnection, request); return httpConnection; } - Task SetupSSL (HttpsURLConnection httpsConnection) + void SetupSSL (HttpsURLConnection httpsConnection) { - return Task.Factory.StartNew (() => { if (httpsConnection == null) return; @@ -514,12 +507,10 @@ Task SetupSSL (HttpsURLConnection httpsConnection) SSLContext context = SSLContext.GetInstance ("TLS"); context.Init (kmf?.GetKeyManagers (), tmf.GetTrustManagers (), null); httpsConnection.SSLSocketFactory = context.SocketFactory; - }); } - Task HandlePreAuthentication (HttpURLConnection httpConnection) + void HandlePreAuthentication (HttpURLConnection httpConnection) { - return Task.Factory.StartNew (() => { AuthenticationData data = PreAuthenticationData; if (!PreAuthenticate || data == null) return; @@ -548,25 +539,21 @@ Task 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); - }); } - Task AddHeaders (HttpURLConnection conn, HttpHeaders headers) + void 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); } - }); } - async Task SetupRequestBody (HttpURLConnection httpConnection, HttpRequestMessage request) + void 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. @@ -576,27 +563,13 @@ async Task SetupRequestBody (HttpURLConnection httpConnection, HttpRequestMessag } return; } - - 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")); + httpConnection.DoOutput = true; + long? contentLength = request.Content.Headers.ContentLength; + if (contentLength != null) + httpConnection.SetFixedLengthStreamingMode ((int)contentLength); else - contentType = "text/plain"; - httpConnection.SetRequestProperty ("Content-Type", contentType); - - await Task.Factory.StartNew (() => { - httpConnection.OutputStream.Write (bytes, 0, contentLength); - httpConnection.OutputStream.Flush (); - httpConnection.OutputStream.Close (); - }); + httpConnection.SetChunkedStreamingMode (0); } } } diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidHttpResponseMessage.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidHttpResponseMessage.cs index 68cbf065d41..3b6e4bb5b3b 100644 --- a/src/Mono.Android/Xamarin.Android.Net/AndroidHttpResponseMessage.cs +++ b/src/Mono.Android/Xamarin.Android.Net/AndroidHttpResponseMessage.cs @@ -9,12 +9,20 @@ namespace Xamarin.Android.Net /// public class AndroidHttpResponseMessage : HttpResponseMessage { + URL javaUrl; + HttpURLConnection httpConnection; + /// /// Set to the same value as . /// /// The requested authentication. public IList RequestedAuthentication { get; internal set; } + public AndroidHttpResponseMessage (URL javaUrl, HttpURLConnection httpConnection) { + javaUrl = javaUrl; + httpConnection = httpConnection; + } + /// /// Set to the same value as /// @@ -22,5 +30,17 @@ public class AndroidHttpResponseMessage : HttpResponseMessage public bool RequestNeedsAuthorization { get { return RequestedAuthentication?.Count > 0; } } + + protected override void Dispose(bool disposing) { + base.Dispose(disposing); + + if (javaUrl != null) { + javaUrl.Dispose (); + } + + if (httpConnection != null) { + httpConnection.Dispose (); + } + } } }