-
Notifications
You must be signed in to change notification settings - Fork 818
Socket transport: Don't close socket on available data #2179
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Socket transport: Don't close socket on available data #2179
Conversation
| // | ||
| // If a socket with data is closed immediately then that might happen continuously. | ||
| // Chill, leave the socket alone, and be happy that it can be read from. We can create a new one later. | ||
| do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just to check my understanding; it looks like this only happens during initial connect, and we're intentionally burning the data (and triggering a failure condition) because this is unexpected, but we want to be able to poll later, and thus want an empty state - is that right?
quick question: why do we explicitly not expect data initially? is that a protocol specification thing, that the server always talks first?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This issue arose because I didn't completely understand the HTTP/2 handshake process.
I thought a client always initializes an HTTP/2 connection by sending the HTTP/2 preface, and then the server responds with a SETTINGS frame. That's the way Kestrel/IIS/HTTP.sys work.
It turns out that some HTTP/2 server implementations don't wait for the preface. They preemptively send the SETTINGS frame. There isn't a rule against doing this.
See https://datatracker.ietf.org/doc/html/rfc7540#section-3.5
mgravell
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
going to mark approved, but: would still love an answer on that question, just to help my understanding
|
Hello. We really need this fix. We have added this fix now separately to version 2.55 and made an internal fork of the lib - this fixes the problem. But we do not want to support the fork on our side - we would like to use the original packages. |
@mgravell I made big changes. Data is cached and replayed so a socket doesn't need to be recreated. Please take another look. |
src/Grpc.Net.Client/Balancer/Internal/SocketConnectivitySubchannelTransport.cs
Show resolved
Hide resolved
src/Grpc.Net.Client/Balancer/Internal/SocketConnectivitySubchannelTransport.cs
Show resolved
Hide resolved
src/Grpc.Net.Client/Balancer/Internal/SocketConnectivitySubchannelTransport.cs
Show resolved
Hide resolved
| _inner.ReadAsync(buffer, cancellationToken); | ||
| public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default) | ||
| { | ||
| if (_initialSocketData != null && _initialSocketData.Count > 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is probably fine since we usually expect at most one initial buffer fragment; however, I wonder if this would be a lot simpler if ReadOnlySequence<byte> was used instead of List<ReadOnlyMemory<byte>> - they're semantically comparable, except a ROS can be sliced more simply
however, if you agree that this isn't high volume, might be better to leave it simple
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't use a ReadOnlySequence because they're a pain to create. But you're right that it would reading easier.
I expect only a small amount of data from the SETTINGS frame. It's likely the list will have one array in it.
mgravell
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Marking approved, but with a few notes - the "lock" one seeming the biggest, but please also consider the timer question
|
@kalduzov A package with this change will be available on the nightly feed in a day or two. You can temporarily use it https://github.com/grpc/grpc-dotnet#grpc-nuget-feed It would be great to check whether your issue is resolved with the latest changes. That would avoid potential months of delay from releasing a new version of grpc-dotnet with a fix, finding out it didn't work in the real world, and having to do follow-up work and waiting for the next release. |
|
@JamesNK Thank you very much. We'll check it out soon. |
|
@JamesNK hello,use this package something wrong like : System.Private.CoreLib\nSystem.Net.Http.HttpRequestException: An HTTP/2 connection could not be established because the server did not complete the HTTP/2 handshake.\n at System.Net.Http.HttpConnectionPool.ReturnHttp2Connection(Http2Connection connection, Boolean isNewConnection)\n at System.Net.Http.HttpConnectionPool.AddHttp2ConnectionAsync(HttpRequestMessage request)\n at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)\n at System.Net.Http.HttpConnectionPool.AddHttp2ConnectionAsync(HttpRequestMessage request)\n at System.Threading.Tasks.Task |
|
Please create a new issue. I haven't seen that error during development. I need a minimal reproduction so I can see it for myself and fix it. Upload a reproduction to GitHub. |
|
Hi @JamesNK. We rolled out a nightly assembly with a fix in production. Everything is stable. When is 2.56.0 scheduled to be released? |
|
Great. Probably be a preview end of this week or next week, and a full version a couple of weeks later. |
Addresses #2161
Addresses #2146
The socket transports regularly checks that a socket is still alive. This PR changes its logic to not close the socket if there is pending data.
The socket is now marked that there was unexpected data and left open. When it comes time to use the socket to create aSocketsHttpHandlerstream, the socket is recreated then.Pending data can happen because some servers (C-core, Java) eagerly send the SETTINGS frame to the client.
Pending data is now cached (up to a limit) and then replayed back to the client when the real connection is established. Fixes the problem and avoids the overhead of having to recreate the socket.