Skip to content
Closed
Show file tree
Hide file tree
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
13 changes: 13 additions & 0 deletions src/Foundation/NSUrlSessionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,19 @@ void DidReceiveChallengeImpl (NSUrlSession session, NSUrlSessionTask task, NSUrl
var credential = new NSUrlCredential (identity, new SecCertificate [] { cert }, NSUrlCredentialPersistence.ForSession);
completionHandler (NSUrlSessionAuthChallengeDisposition.UseCredential, credential);
return;
} else if (!AppContext.TryGetSwitch ("Foundation.NSUrlSessionHandler.NoMissingCertificateHandling", out bool enabled) || !enabled) {
// The server requested a certificate, but we don't have one to provide. Fail the request with a meaningful exception
// that allows the developer to identify this, ask the user for a certificate, add it to the ClientCertificates collection
// and then re-try the request.
lock (inflight.Lock) {
inflight.Exception = new HttpRequestException ("An error occurred while sending the request.",
new WebException ("Error: Certificate Required",
new AuthenticationException ("Error: Certificate Required"),
WebExceptionStatus.SecureChannelFailure, null));
}
// We will still continue with a null credential, since some services uses optional client certificates and this will still let it succeed
completionHandler (NSUrlSessionAuthChallengeDisposition.PerformDefaultHandling, null!);
return;
}
}

Expand Down
40 changes: 40 additions & 0 deletions tests/monotouch-test/System.Net.Http/MessageHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,46 @@ public void TestNSUrlSessionHandlerSendClientCertificate ()
}
}

[Test]
public void TestNSUrlSessionHandlerOptionalClientCertificate ()
{
string content = "";
var done = TestRuntime.TryRunAsync (TimeSpan.FromSeconds (30), async () => {
using var handler = new NSUrlSessionHandler ();
using var client = new HttpClient (handler);
// This service doesn't require a certificate and should succeed even if one isn't provided
var response = await client.GetAsync (NetworkResources.EchoClientCertificateUrl);
content = await response.EnsureSuccessStatusCode ().Content.ReadAsStringAsync ();
}, out var ex);
if (!done) { // timeouts happen in the bots due to dns issues, connection issues etc.. we do not want to fail
Assert.Inconclusive ("Request timedout.");
} else {
Assert.IsNull (ex, "Exception wasn't expected.");
}
}

[Test]
public void TestNSUrlSessionHandlerDetectMissingClientCertificate ()
{
string content = "";
var done = TestRuntime.TryRunAsync (TimeSpan.FromSeconds (30), async () => {
using var handler = new NSUrlSessionHandler ();
using var client = new HttpClient (handler);
// TODO: Replace with a service that actually requires a certificate and uses TLS1.2
var response = await client.GetAsync (NetworkResources.EchoClientCertificateUrl);
content = await response.EnsureSuccessStatusCode ().Content.ReadAsStringAsync ();
}, out var ex);
if (!done) { // timeouts happen in the bots due to dns issues, connection issues etc.. we do not want to fail
Assert.Inconclusive ("Request timedout.");
} else {
Assert.IsNotNull (ex, "Exception was expected.");
Assert.IsInstanceOf (typeof (HttpRequestException), ex, "Exception");
Assert.IsInstanceOf (typeof (WebException), ex.InnerException, "InnerException Type");
Assert.AreEqual (WebExceptionStatus.SecureChannelFailure, ((WebException) ex.InnerException).Status, "InnerException Status");
Assert.IsInstanceOf (typeof (AuthenticationException), ex.InnerException.InnerException, "InnerException.InnerException Type");
}
}

[Test]
public void AssertDefaultValuesNSUrlSessionHandler ()
{
Expand Down
Loading