-
Notifications
You must be signed in to change notification settings - Fork 362
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
pkgs/ok_http: Close and remove all idle connections from the resource pool on response #1223
Conversation
@@ -126,6 +126,9 @@ class OkHttpClient extends BaseClient { | |||
request: request, | |||
contentLength: contentLength, | |||
)); | |||
|
|||
// Close and remove all idle connections from the resource pool. | |||
_client.connectionPool().evictAll(); |
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 read your explanation but I still don't understand why this is necessary. Since the socket is closed by the server after receiving the headers, how can it be reused?
await socket.close(); |
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.
The problem here is that even if the socket is closed, the server keeps listening to the request
request.listen((line)) {
and even when you encounter an empty line, it only adds the cookies
list to the channel sink.
This seemed very peculiar to me, but I confirmed that it was indeed the problem by doing:
If you create a StreamSubscription and cancel it when an empty line is encountered.
late StreamSubscription reqSubscription;
reqSubscription = request.listen((line) {
if (line.toLowerCase().startsWith('cookie:')) {
cookies.add(line);
}
if (line.isEmpty) {
// A blank line indicates the end of the headers.
channel.sink.add(cookies);
reqSubscription.cancel(); // <- here
}
});
then all works well, without the need of explicitly removing idle connections.
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 what I used for reference: https://stackoverflow.com/questions/31183730/okhttp-asynchronous-request-doesnt-stop-immediately-after-onresponse
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.
Hmmm... the test seems racy. Does this formulation work:
await for (final line in request) {
if (line.toLowerCase().startsWith('cookie:')) {
cookies.add(line);
}
if (line.isEmpty) {
// A blank line indicates the end of the headers.
channel.sink.add(cookies);
break;
}
}
socket.writeAll(...)
await socket.close()
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.
Yes, it does!
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.
Do you suggest changing the test case instead? I didn't go that route because CronetClient
seems to pass both cases without a problem.
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.
Do you suggest changing the test case instead? I didn't go that route because
CronetClient
seems to pass both cases without a problem.
Yeah, I think that the test is incorrect and it is a coincidence that it works ;-)
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.
Alright, then it's good to close this PR. We would still need okhttp3.ConnectionPool
and evictAll()
for properly closing the client if I'm not wrong.
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.
Alright, then it's good to close this PR. We would still need
okhttp3.ConnectionPool
andevictAll()
for properly closing the client if I'm not wrong.
You probably want to empty the connection pool when in response to Client.close()
. From the docs
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.
Yeah, that's exactly what I was going to follow.
- call shutdown on
ExecutorService
- empty the connection pool
- release client object (JNI)
Items in this PR:
okhttp3.ConnectionPool
(allows us to use theevictAll()
method)onResponse()
testRequestCookies()
, the server determines the requests only by binding on the socket. And, OkHttp never frees or closes connections for allenqueue()
calls. This led to the server assuming multiple requests as a single entity (as OkHttp never stopped communicating with the socket,) and parsing bothcookie
headers as one. This gave a cookies list with a length of 2, which failed the 2nd test casemultiple cookies semicolon separated
. By explicitly closing idle connections, we protect consecutive requests from being overlapped.testRequestCookies()
testResponseHeaders()
since all TCs are passing now that [http_client_conformance_tests] Add flag to skip tests for folded headers #1219 is closed (see pkgs/http_client_conformance_tests: add boolean flagsupportsFoldedHeaders
#1222)Contribution guidelines:
dart format
.Note that many Dart repos have a weekly cadence for reviewing PRs - please allow for some latency before initial review feedback.