Open yahya-uddin opened 4 years ago
No response? I'm interested in this as well.
i wanna know this too as i have an application with a lot of api requests and which takes time in getting the response and whenever abruptly the screen is changed or another api is called before the earlier request was completed it hangs and shows an error Connection Closed Before Full Header was received. I have been stuck on this for very long any help is appreciated!
I have been facing the same issue when making api requests. I couldn't close the connection when my app navigate to other pages which makes my app to be hanging. Any form of help or explanation will be appreciated
Hmm. I'm also following this. In my app I have a singleton http client that I initialize once. I then call this client synchronously with get/post/puts. I never close it. This seems to work just fine and I've made several hundreds calls without any issues. However. My end users have reported that the app seemed to stop talking to the server after a while but the animations and buttons were still working. Just that all requests times out. This problem occured after a while and what seems to be at random. I've debugged this for quite some time and at one point I got a state where I called myHttpClient.get() but I did not receive the request server side. After 30 seconds (my configured timeout) the request timed out. I could keep on sending requests to the server but none of these requests reached the server. It seemed like that http client was disposed but I did not get any exception other than requests timing out. So I figured, this is probably what my end users are experiencing. That all http requests suddenly just time out. Could this be because of some pool of requests being "filled up" with broken requests? If the wifi is shaky and a requests break, maybe that pool slot is "broken" and after a while all pool slots are broken and the http client is completely broken?
Would a better practice be to initialize a new HttpClient for every request and closing that after each request? Any downsides?
@mikeesouth one obvious downside is that it's much slower than using an existing client (about 2x slower in my rough testing). I also have a production app that I never close the connection and it seems that it doesn't have your problem.
What i will do is keep open a persistent connection by using a Client rather than making a one-off request since multiple requests are sent to the same server
var client = http.Client();
try {
var uriResponse = await client.post(Uri.parse('https://url.com'),body: {''});
} finally {
client.close();
}
In terms of network traffic, it's better to use the same client throughout the app lifecycle. Establishing a new connection for each api is very expensive. However, as per the documentation,
It's important to close each client when it's done being used; failing to do so can cause the Dart process to hang.
Also, if client.close() isn't called, it doesn't mean that the server will keep the connection open forever. The server will close the connection if it is idle for a period more than the HTTP Keep-Alive Timeout. In this case, if the client sends a new request over the connection closed by server, he'll get a 408 Request Timeout.
So, if you decide to use the same client throughout the app lifecycle, keep in your mind the two possible issues that you may run into.
From @mikeesouth comment
This problem occured after a while and what seems to be at random. I've debugged this for quite some time and at one point I got a state where I called myHttpClient.get() but I did not receive the request server side. After 30 seconds (my configured timeout) the request timed out. I could keep on sending requests to the server but none of these requests reached the server.
We encountered this issue as well and narrowed it down to the following problem: https://stackoverflow.com/questions/66063913/socketexception-failed-host-lookup-com-os-error-nodename-nor-servname-p
In our case we had a HttpService class to perform get, put, post, etc. requests (adding headers etc.) - that was used throughout the app. We added a counter to record the request count and instanciate a new instance every 200 times (or something like that) and only if no pending calls are present (otherwise the request fails with Client already closed
):
...
import 'package:http/http.dart' as http;
...
class HttpService {
// api call threshold
// create a new instance of http client after a minimum given number of calls have been made
// needed, because otherwise SocketException occurs after some time
static int _calls = 0;
static int _minCallsBeforeNewInstance = 200;
// record ongoing calls - needed otherwise a forced close and new instance of http client may cause problems with pending calls
static int _ongoingCallsCount = 0;
static http.Client _httpClient = http.Client();
static final HttpService _instance = HttpService._();
HttpService._();
factory HttpService() {
return _instance;
}
...
Future<http.Response> get(
Uri url, {
Map<String, String>? headers,
}) async {
_calls++;
_ongoingCallsCount++;
Logger.debug('GET ($_calls): $url');
return _httpClient
.get(
url,
headers: await _headers(headers),
)
.whenComplete(_handleCallComplete);
}
...
void _handleCallComplete() {
if (_ongoingCallsCount > 0) _ongoingCallsCount--;
if (_ongoingCallsCount < 0) _ongoingCallsCount = 0;
// create new instance only if no pending calls are out there and the minimum calls have been reached
if (_ongoingCallsCount == 0 && _calls >= _minCallsBeforeNewInstance) {
Logger.debug('Closing existing http client and create new one');
_httpClient.close();
_httpClient = http.Client();
// reset calls
_calls = 0;
}
}
}
@RANSoftRA , Have you found that your posted solution resolves the host lookup exception issue that you linked to?
@RANSoftRA , Have you found that your posted solution resolves the host lookup exception issue that you linked to?
Yes, the linked issue was solved with this approach.
It is client.close();
with _withClient
. If you request continuously, client.close();
will be repeated, so in that case
Should I use a Client object and close it as soon as I'm done with it????
_withClient
https://github.com/dart-lang/http/blob/06649afbb5847dbb0293816ba8348766b116e419/pkgs/http/lib/http.dart#L166
https://pub.dev/packages/http#using https://github.com/dart-lang/http/blob/06649afbb5847dbb0293816ba8348766b116e419/pkgs/http/lib/http.dart
@mikeesouth one obvious downside is that it's much slower than using an existing client (about 2x slower in my rough testing). I also have a production app that I never close the connection and it seems that it doesn't have your problem.
Same issue for me, have implemented the suggested.
Could it be the addition of background_fetch, background_processing capabilities that keep the http instance alive so long?
4 years :|
@natebosch @brianquinlan @kevmoo I just dug deeper into this issue because I'm trying to use the new cronet and cupertino Httpclients. The example at https://github.com/dart-lang/http/blob/master/pkgs/flutter_http_example/lib/http_client_factory.dart returns always new instances of the proxy which leads to the question that is also asked here for such a long time:
client.close()
?What is the best strategy to keep connections alive?
Aren't some of/those questions answered by the comments on the http methods? Ref: https://github.com/dart-lang/http/blob/06649afbb5847dbb0293816ba8348766b116e419/pkgs/http/lib/http.dart#L121C30-L123C4
This still doesn't give a clear answer at least not for my understanding see this full thread Am 12. Juni 2024, 21:19 +0100 schrieb Pedro Massango @.***>:
Aren't some of/those questions answered by the comments on the http methods? Ref: https://github.com/dart-lang/http/blob/06649afbb5847dbb0293816ba8348766b116e419/pkgs/http/lib/http.dart#L121C30-L123C4 Screenshot.2024-06-12.at.10.12.15.PM.png (view on web) — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***>
@natebosch @brianquinlan @kevmoo I just dug deeper into this issue because I'm trying to use the new cronet and cupertino Httpclients. The example at https://github.com/dart-lang/http/blob/master/pkgs/flutter_http_example/lib/http_client_factory.dart returns always new instances of the proxy
You mean a new instance of the Client
, right? A new Client
is not created for every HTTP request - it is created once when it is first accessed. I'll add a comment explaining that.
which leads to the question that is also asked here for such a long time:
- How many HTTPClients should an app use?
Probably one would be fine for most applications. You may need more if you have different use cases where Client
s with different properties are required. For example, you might want a separate client to download large assets with package:cupertino_http
so that allowsConstrainedNetworkAccess can be enabled.
- a new one for every request?
No.
- separate ones if you do requests from different servers?
That should not be necessary except in the case of the exception that I mentioned above.
- separate ones if you use isolates so that every isolate gets its own?
You will need a new Client
per isolate because Clients
are not shareable between isolates (even if they are in the same isolate group).
- use the same one everywhere? which pros/cons to these options are there?
- do and when do we have to call
client.close()
?
You should call client.close()
when you are done with the client. Depending on the client, this may cause the Client
s connection pool to be released and may release significant native resources. If you only have a single Client
then, in flutter, I wouldn't worry about closing it.
- Should a client be closed and re-opened when the app suspends and resumes respectively?
IOClient
connections are not valid after network changes, proxy changes, etc. but I think that you should just not use IOClient
on mobile devices.
- Or should it be closed only when the app is entirely closed?
I think that we covered this one.
- If yes to the previous question, should it be done explicitly?
I think that we covered this one.
What is the best strategy to keep connections alive?
I'm don't have a good answer for that.
thanks a lot @brianquinlan Ok, so to summarize:
If you don't need some special behavior clients 1 Client for the full app should be anough and it doesn't have any impact of the performance even if we do multiple async network calls on the same client in parallel and one client is able to handle multiple Http conenctions that use the keep-alive feature. and if we only use one we don't even have to close it manually because if the app closes it will be destroyed.
The second paragraph in the API dos might be the reason people are not sure what to do:
Could you explain what is really meant by this because it seems to contradict what I understand from your reply.
I hope the connection pool will shrink once the connections are no longer used?
Could you explain what is really meant by this because it seems to contradict what I understand from your reply. I hope the connection pool will shrink once the connections are no longer used?
Programs run using the Dart CLI will not exit while there are pending asynchronous operations. IOClient
maintains a connection pool so the Dart CLI will not exit while there are still connections in the connection pool. How long a connection is kept active when not used is controlled by HttpClient.idleTimeout
but the default is 15 seconds.
Other clients have different behavior.
If I understand correctly, Flutter doesn't care about pending asynchronous operations when deciding whether the application should terminate (and mobile operation systems can kill an application at anytime anyway).
Ah, that makes sense. I was already guessing that this might only apply to Dart clip clients. And if they will maximal block whole a connection is still active that should rarely make a problem. Maybe that API comment could get addition that explains this. Will write an article over the weekend Am 14. Juni 2024, 19:59 +0100 schrieb Brian Quinlan @.***>:
Could you explain what is really meant by this because it seems to contradict what I understand from your reply. I hope the connection pool will shrink once the connections are no longer used? Programs run using the Dart CLI will not exit while there are pending asynchronous operations. IOClient maintains a connection pool so the Dart CLI will not exit while there are still connections in the connection pool. How long a connection is kept active when not used is controlled by HttpClient.idleTimeout but the default is 15 seconds. Other clients have different behavior. If I understand correctly, Flutter doesn't care about pending asynchronous operations when deciding whether the application should terminate (and mobile operation systems can kill an application at anytime anyway). — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***>
According to the docs it says:
However, in most mobile apps, you tend to send most requests to a single server.
Does that mean, for an app that does a lot of API calls to a server, that we should keep the connection up throughout the app lifetime?
Also should we close and re-open the connection when the app suspends and resume states?