Runtime: Unfriendly error when calling HTTP/2 endpoint with mismatched TLS settings

Created on 4 May 2019  路  6Comments  路  Source: dotnet/runtime

I have a .NET Core client calling a HTTP/2 server. The server has TLS enabled. When I call the server without TLS enabled on the client (I was not using TLS in dev and forgot to change it for the test environment which has TLS) I get an unfriendly error from SendAsync.

Code:

AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2Support", true);

var httpClientHandler = new HttpClientHandler();
httpClientHandler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true;

var httpClient = new HttpClient(httpClientHandler);
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "http://localhost:8080");
var response = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead);

Error:

Unhandled Exception: System.AggregateException: One or more errors occurred. (An error occurred while sending the request.) ---> System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'CreditManager'.
   at System.Net.Http.CreditManager.RequestCreditAsync(Int32 amount, CancellationToken cancellationToken)
   at System.Net.Http.Http2Connection.SendHeadersAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at System.Net.Http.Http2Connection.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.Http2Connection.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)

I would have expected an error saying that the server requires TLS and TLS is not enabled for this call in the client.

This is lower priority. Changing the client app to use https fixed the problem.

Environment:

.NET Core SDK (reflecting any global.json):
 Version:   3.0.100-preview6-011694
 Commit:    a5312773db

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.17134
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\3.0.100-preview6-011694\

Host (useful for support):
  Version: 3.0.0-preview6-27629-07
  Commit:  a3967b6096
area-System.Net.Http

Most helpful comment

@stephentoub why not enumerate the most common causes of this error in the exception message? E.g. "Please ensure that..."

All 6 comments

Can you please try current build? I think this was fixed last week but you may not have it in your build.

I get this error with the current build:

Unhandled Exception: System.AggregateException: One or more errors occurred. (An error occurred while sending the request.) ---> System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.IO.IOException: The response ended prematurely, with at least 9 additional bytes expected.
   at System.Net.Http.Http2Connection.ReadAtLeastAsync(Stream stream, Memory`1 buffer, Int32 minReadBytes)
   at System.Net.Http.Http2Connection.EnsureIncomingBytesAsync(Int32 minReadBytes)
   at System.Net.Http.Http2Connection.ReadFrameAsync(Boolean initialFrame)
   at System.Net.Http.Http2Connection.ProcessIncomingFramesAsync()
   --- End of inner exception stack trace ---
   at System.Net.Http.Http2Connection.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)

Is that what you expected? It doesn't seem particularly informative.

There's a reason unencrypted HTTP/2 was hotly debated, isn't required to be supported, and isn't supported by a variety of clients and servers (and is unsupported and requires setting an environment variable / switch to enable with HttpClient). It's an entirely different protocol, and if the client and server don't have a priori knowledge that both will speak HTTP/2 instead of HTTP/1.1, everything ends up garbled. I don't think it's worth investing in trying to somehow improve this error condition.

note, that now we also have more details when tracing is enabled. I'm not sure how informative the trace is but at least it is distinct and describes external condition rather than error on internals.
I'm gonna close it for now per @stephentoub recommendation.

Ok, no problem. I thought that decision might be the outcome.

@stephentoub why not enumerate the most common causes of this error in the exception message? E.g. "Please ensure that..."

Was this page helpful?
0 / 5 - 0 ratings