Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 50 additions & 32 deletions src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public class AndroidClientHandler : HttpClientHandler
bool decompress_here;

URL java_url;
URLConnection java_connection;
HttpURLConnection java_connection;

/// <summary>
/// <para>
Expand Down Expand Up @@ -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 <HttpResponseMessage> ProcessRequest (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken)
Task <HttpResponseMessage> ProcessRequest (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested ();
httpConnection.InstanceFollowRedirects = AllowAutoRedirect;
RequestedAuthentication = null;
ProxyAuthenticationRequested = false;

return await Task<HttpResponseMessage>.Factory.StartNew (() => DoProcessRequest (request, httpConnection, cancellationToken), cancellationToken).ConfigureAwait (false);
return DoProcessRequest (request, httpConnection, cancellationToken);
}

HttpResponseMessage DoProcessRequest (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken)
async Task <HttpResponseMessage> 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) {
Expand Down Expand Up @@ -372,9 +369,9 @@ void CopyHeaders (HttpURLConnection httpConnection, HttpResponseMessage response
/// </summary>
/// <param name="request">Request data</param>
/// <param name="conn">Pre-configured connection instance</param>
protected virtual void SetupRequest (HttpRequestMessage request, HttpURLConnection conn)
protected virtual Task SetupRequest (HttpRequestMessage request, HttpURLConnection conn)
{
AssertSelf ();
return Task.Factory.StartNew (AssertSelf);
}

/// <summary>
Expand Down Expand Up @@ -433,20 +430,20 @@ void AppendEncoding (string encoding, ref List <string> list)
list.Add (encoding);
}

HttpURLConnection SetupRequestInternal (HttpRequestMessage request, URLConnection conn)
async Task <HttpURLConnection> SetupRequestInternal (HttpRequestMessage request, HttpURLConnection conn)
{
if (conn == null)
throw new ArgumentNullException (nameof (conn));
var httpConnection = conn.JavaCast <HttpURLConnection> ();
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 <string> accept_encoding = null;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<string, IEnumerable<string>> 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 ();
});
}
}
}