Hi,
I’m running through some of the tutorials on your site using .NET Core 2.0 on a Mac. For some reason I’m able to query by ID but when I run a N1QL query I get the following error:
{System.PlatformNotSupportedException: The handler does not support custom handling of certificates with this combination of libcurl (7.54.0) and its SSL backend (“SecureTransport”). at System.Net.Http.CurlHandler.SslProvider.SetSslOptions(EasyRequest easy, ClientCertificateOption clientCertOption) at System.Net.Http.CurlHandler.EasyRequest.InitializeCurl() at System.Net.Http.CurlHandler.MultiAgent.ActivateNewRequest(EasyRequest easy)— End of stack trace from previous location where exception was thrown — at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at System.Net.Http.HttpClient.<FinishSendAsyncBuffered>d__58.MoveNext()--- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Couchbase.N1QL.QueryClient.<ExecuteQueryAsync>d__15
1.MoveNext()}
I saw a few posts related to the version of libcurl used in previous versions of .NET Core but it was my understanding that the underlying issue with HttpClient had been resolved in .NET Core 2.0.
To circumvent this we need to override the ServerCertificateCustomValidationCallback
on the inner handler but since this is buried inside the CouchbaseHttpClient we’re not able to it:
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
}
httpClient = new HttpClient(handler)
You can read more details on the issue here.
Unfortunately, without this we’re unable to use Couchbase on Linux too which makes it pretty much unusable with our stack.
We ran into the same problem. Here’s the solution I came up with after several long hours of reverse engineering. I realize this is ugly as sin, but it works.
This is an extension method on the Couchbase IBucket class. Put this somewhere in your solution.
public static class CouchbaseBucketExtensions
{
public static void HackOutClientCertCallbacks(this IBucket bucket)
{
// Get all the types we need.
var bucketType = typeof(CouchbaseBucket);
var couchbaseConfigContextType = bucketType.Assembly
.GetType("Couchbase.Configuration.CouchbaseConfigContext");
var serverType = bucketType.Assembly
.GetType("Couchbase.Core.Server");
var queryClientType = bucketType.Assembly
.GetType("Couchbase.N1QL.QueryClient");
var streamingQueryClientType = bucketType.Assembly
.GetType("Couchbase.N1QL.StreamingQueryClient");
var couchbaseHttpClientType = bucketType.Assembly
.GetType("Couchbase.IO.Http.CouchbaseHttpClient");
// Follow me down the rabbit hole...
var configContext = typeof(CouchbaseBucket)
.GetField("_configInfo", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(bucket);
var queryNodes = (List<IServer>)couchbaseConfigContextType
.GetField("QueryNodes", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(configContext);
foreach (var queryNode in queryNodes)
{
// Query nodes have a streaming handler and a non streaming handler. We want to
// zero out the callback on both.
var streamingQueryClient = serverType
.GetField("_streamingQueryClient", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(queryNode);
var streamingHttpClient = (HttpMessageInvoker)streamingQueryClientType
.GetProperty("HttpClient", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(streamingQueryClient);
var streamingHandler = (HttpClientHandler)typeof(HttpMessageInvoker)
.GetField("_handler", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(streamingHttpClient);
// Null out the custom cert callback on the handler.
streamingHandler.ServerCertificateCustomValidationCallback = null;
var httpClient = (HttpMessageInvoker)queryClientType
.GetProperty("HttpClient", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(queryNode.QueryClient);
var handler = (HttpClientHandler)typeof(HttpMessageInvoker)
.GetField("_handler", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(httpClient);
// Null out the custom cert callback on the handler.
handler.ServerCertificateCustomValidationCallback = null;
}
}
}
I call this from my Startup.cs class when running on a development environment. You call it like this
ClusterHelper.GetBucket(DefaultBucketName).HackOutClientCertCallbacks();
Hope that helps!
@benfoster - what client version are you using? Hmm, if this is fixed in .NET Core 2.0, perhaps for some reason your still targeting 1.x?
@ryan.thompson - creative
It might make sense to open this up a bit so you can plug your own callback (or null it out) - I created a jira ticket for this: NCBC-1620. Feel free to send a PR if you want to contribute, otherwise its scheduled tentatively for v2.5.6.
It’s definitely 2.0 but it looks like it hasn’t been fixed yet.
Ideally we should be able to override how the entire underlying HttpClientHandler/HttpClient is created. I haven’t looked that deep into the SDK code but we usually share the same HttpClient instance for better performance, which is not possible currently.
The SDK reuses HttpClient’s instances internally.