Skip to content

Commit ca89d85

Browse files
committed
[Mono.Android, Xamarin.Android.Net.AndroidClientHandler] Updated to work with requests which contains content. Also fixed a race condition which causes the Output stream to not be ready. Fixed issues with calling on the MainThread which is not alowed with the Java HttpURLConnection
updating logger incorrect calls adding fixes
1 parent 1b3a76c commit ca89d85

File tree

1 file changed

+50
-32
lines changed

1 file changed

+50
-32
lines changed

src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs

Lines changed: 50 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public class AndroidClientHandler : HttpClientHandler
9191
bool decompress_here;
9292

9393
URL java_url;
94-
URLConnection java_connection;
94+
HttpURLConnection java_connection;
9595

9696
/// <summary>
9797
/// <para>
@@ -189,33 +189,30 @@ protected void AssertSelf ()
189189
if (!request.RequestUri.IsAbsoluteUri)
190190
throw new ArgumentException ("Must represent an absolute URI", "request");
191191

192-
/*using (*/java_url = new URL (request.RequestUri.ToString ());/*) {*/
193-
/*using (*/java_connection = java_url.OpenConnection ();/*) {*/
194-
HttpURLConnection httpConnection = SetupRequestInternal (request, java_connection);
192+
java_url = new URL (request.RequestUri.ToString ());
193+
java_connection = java_url.OpenConnection () as HttpURLConnection;
194+
HttpURLConnection httpConnection = await SetupRequestInternal (request, java_connection).ConfigureAwait (false);
195195
return await ProcessRequest (request, httpConnection, cancellationToken);
196-
/*}
197-
}*/
198196
}
199197

200-
async Task <HttpResponseMessage> ProcessRequest (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken)
198+
Task <HttpResponseMessage> ProcessRequest (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken)
201199
{
202200
cancellationToken.ThrowIfCancellationRequested ();
203201
httpConnection.InstanceFollowRedirects = AllowAutoRedirect;
204202
RequestedAuthentication = null;
205203
ProxyAuthenticationRequested = false;
206204

207-
return await Task<HttpResponseMessage>.Factory.StartNew (() => DoProcessRequest (request, httpConnection, cancellationToken), cancellationToken).ConfigureAwait (false);
205+
return DoProcessRequest (request, httpConnection, cancellationToken);
208206
}
209207

210-
HttpResponseMessage DoProcessRequest (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken)
208+
async Task <HttpResponseMessage> DoProcessRequest (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken)
211209
{
212210
if (Logger.LogNet)
213211
Logger.Log (LogLevel.Info, LOG_APP, $"{this}.DoProcessRequest ()");
214-
httpConnection.RequestMethod = request.Method.ToString ();
215212
try {
216213
if (Logger.LogNet)
217214
Logger.Log (LogLevel.Info, LOG_APP, $" connecting");
218-
httpConnection.Connect ();
215+
await httpConnection.ConnectAsync ().ConfigureAwait (false);
219216
if (Logger.LogNet)
220217
Logger.Log (LogLevel.Info, LOG_APP, $" connected");
221218
} catch (Java.Net.ConnectException ex) {
@@ -372,9 +369,9 @@ void CopyHeaders (HttpURLConnection httpConnection, HttpResponseMessage response
372369
/// </summary>
373370
/// <param name="request">Request data</param>
374371
/// <param name="conn">Pre-configured connection instance</param>
375-
protected virtual void SetupRequest (HttpRequestMessage request, HttpURLConnection conn)
372+
protected virtual Task SetupRequest (HttpRequestMessage request, HttpURLConnection conn)
376373
{
377-
AssertSelf ();
374+
return Task.Factory.StartNew (AssertSelf);
378375
}
379376

380377
/// <summary>
@@ -433,20 +430,20 @@ void AppendEncoding (string encoding, ref List <string> list)
433430
list.Add (encoding);
434431
}
435432

436-
HttpURLConnection SetupRequestInternal (HttpRequestMessage request, URLConnection conn)
433+
async Task <HttpURLConnection> SetupRequestInternal (HttpRequestMessage request, HttpURLConnection conn)
437434
{
438435
if (conn == null)
439436
throw new ArgumentNullException (nameof (conn));
440-
var httpConnection = conn.JavaCast <HttpURLConnection> ();
437+
var httpConnection = conn;
441438
if (httpConnection == null)
442439
throw new InvalidOperationException ($"Unsupported URL scheme {conn.URL.Protocol}");
443-
440+
httpConnection.RequestMethod = request.Method.ToString ();
444441
// SSL context must be set up as soon as possible, before adding any content or
445442
// headers. Otherwise Java won't use the socket factory
446-
SetupSSL (httpConnection as HttpsURLConnection);
443+
await SetupSSL (httpConnection as HttpsURLConnection);
447444
if (request.Content != null)
448-
AddHeaders (httpConnection, request.Content.Headers);
449-
AddHeaders (httpConnection, request.Headers);
445+
await AddHeaders (httpConnection, request.Content.Headers);
446+
await AddHeaders (httpConnection, request.Headers);
450447

451448
List <string> accept_encoding = null;
452449

@@ -475,15 +472,16 @@ HttpURLConnection SetupRequestInternal (HttpRequestMessage request, URLConnectio
475472
httpConnection.SetRequestProperty ("Cookie", cookieHeaderValue);
476473
}
477474

478-
HandlePreAuthentication (httpConnection);
479-
SetupRequest (request, httpConnection);
480-
SetupRequestBody (httpConnection, request);
475+
await HandlePreAuthentication (httpConnection);
476+
await SetupRequest (request, httpConnection);
477+
await SetupRequestBody (httpConnection, request);
481478

482479
return httpConnection;
483480
}
484481

485-
void SetupSSL (HttpsURLConnection httpsConnection)
482+
Task SetupSSL (HttpsURLConnection httpsConnection)
486483
{
484+
return Task.Factory.StartNew (() => {
487485
if (httpsConnection == null)
488486
return;
489487

@@ -516,10 +514,12 @@ void SetupSSL (HttpsURLConnection httpsConnection)
516514
SSLContext context = SSLContext.GetInstance ("TLS");
517515
context.Init (kmf?.GetKeyManagers (), tmf.GetTrustManagers (), null);
518516
httpsConnection.SSLSocketFactory = context.SocketFactory;
517+
});
519518
}
520519

521-
void HandlePreAuthentication (HttpURLConnection httpConnection)
520+
Task HandlePreAuthentication (HttpURLConnection httpConnection)
522521
{
522+
return Task.Factory.StartNew (() => {
523523
AuthenticationData data = PreAuthenticationData;
524524
if (!PreAuthenticate || data == null)
525525
return;
@@ -548,37 +548,55 @@ void HandlePreAuthentication (HttpURLConnection httpConnection)
548548
if (Logger.LogNet)
549549
Logger.Log (LogLevel.Info, LOG_APP, $"Authentication header '{data.UseProxyAuthentication ? "Proxy-Authorization" : "Authorization"}' will be set to '{authorization.Message}'");
550550
httpConnection.SetRequestProperty (data.UseProxyAuthentication ? "Proxy-Authorization" : "Authorization", authorization.Message);
551+
});
551552
}
552553

553-
void AddHeaders (HttpURLConnection conn, HttpHeaders headers)
554+
Task AddHeaders (HttpURLConnection conn, HttpHeaders headers)
554555
{
556+
return Task.Factory.StartNew (() => {
555557
if (headers == null)
556558
return;
557559

558560
foreach (KeyValuePair<string, IEnumerable<string>> header in headers) {
559561
conn.SetRequestProperty (header.Key, header.Value != null ? String.Join (",", header.Value) : String.Empty);
560562
}
563+
});
561564
}
562565

563-
void SetupRequestBody (HttpURLConnection conn, HttpRequestMessage request)
566+
async Task SetupRequestBody (HttpURLConnection httpConnection, HttpRequestMessage request)
564567
{
565568
if (request.Content == null) {
569+
httpConnection.SetChunkedStreamingMode (0);
566570
// Pilfered from System.Net.Http.HttpClientHandler:SendAync
567571
if (HttpMethod.Post.Equals (request.Method) || HttpMethod.Put.Equals (request.Method) || HttpMethod.Delete.Equals (request.Method)) {
568572
// Explicitly set this to make sure we're sending a "Content-Length: 0" header.
569573
// This fixes the issue that's been reported on the forums:
570574
// http://forums.xamarin.com/discussion/17770/length-required-error-in-http-post-since-latest-release
571-
conn.SetRequestProperty ("Content-Length", "0");
575+
httpConnection.SetRequestProperty ("Content-Length", "0");
572576
}
573577
return;
574578
}
575579

576-
conn.DoOutput = true;
577-
long? contentLength = request.Content.Headers.ContentLength;
578-
if (contentLength != null)
579-
conn.SetFixedLengthStreamingMode ((int)contentLength);
580+
httpConnection.DoOutput = true;
581+
582+
var bytes = await request.Content.ReadAsByteArrayAsync ().ConfigureAwait (false);
583+
584+
int contentLength = bytes.Length;
585+
httpConnection.SetRequestProperty ("Content-Length", contentLength.ToString());
586+
httpConnection.SetFixedLengthStreamingMode (contentLength);
587+
588+
string contentType;
589+
if (request.Content.Headers.ContentType != null)
590+
contentType = String.Join (" ", request.Content.Headers.GetValues ("Content-Type"));
580591
else
581-
conn.SetChunkedStreamingMode (0);
592+
contentType = "text/plain";
593+
httpConnection.SetRequestProperty ("Content-Type", contentType);
594+
595+
await Task.Factory.StartNew (() => {
596+
httpConnection.OutputStream.Write (bytes, 0, contentLength);
597+
httpConnection.OutputStream.Flush ();
598+
httpConnection.OutputStream.Close ();
599+
});
582600
}
583601
}
584602
}

0 commit comments

Comments
 (0)