From: Stefan Eissing Date: Wed, 6 Nov 2024 11:42:34 +0000 (+0100) Subject: pytest: add test for use of CURLMOPT_MAX_HOST_CONNECTIONS X-Git-Tag: curl-8_11_1~32 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=55968fd14ba6a503a8822ab40d09ad0bd0826dd5;p=thirdparty%2Fcurl.git pytest: add test for use of CURLMOPT_MAX_HOST_CONNECTIONS Add test_02_33 to run with various values for the multi option CURLMOPT_MAX_HOST_CONNECTIONS and CURLOPT_FRESH_CONNECT to trigger connection pool limit handling code. Closes #15494 --- diff --git a/tests/http/clients/hx-download.c b/tests/http/clients/hx-download.c index de50e273e2..94e9d0cbd6 100644 --- a/tests/http/clients/hx-download.c +++ b/tests/http/clients/hx-download.c @@ -229,7 +229,7 @@ static int my_progress_cb(void *userdata, static int setup(CURL *hnd, const char *url, struct transfer *t, int http_version, struct curl_slist *host, - CURLSH *share, int use_earlydata) + CURLSH *share, int use_earlydata, int fresh_connect) { curl_easy_setopt(hnd, CURLOPT_SHARE, share); curl_easy_setopt(hnd, CURLOPT_URL, url); @@ -248,6 +248,8 @@ static int setup(CURL *hnd, const char *url, struct transfer *t, curl_easy_setopt(hnd, CURLOPT_FORBID_REUSE, 1L); if(host) curl_easy_setopt(hnd, CURLOPT_RESOLVE, host); + if(fresh_connect) + curl_easy_setopt(hnd, CURLOPT_FRESH_CONNECT, 1L); /* please be verbose */ if(verbose) { @@ -304,9 +306,11 @@ int main(int argc, char *argv[]) int http_version = CURL_HTTP_VERSION_2_0; int ch; struct curl_slist *host = NULL; - const char *resolve = NULL; + char *resolve = NULL; + size_t max_host_conns = 0; + int fresh_connect = 0; - while((ch = getopt(argc, argv, "aefhm:n:A:F:P:r:V:")) != -1) { + while((ch = getopt(argc, argv, "aefhm:n:xA:F:M:P:r:V:")) != -1) { switch(ch) { case 'h': usage(NULL); @@ -326,17 +330,23 @@ int main(int argc, char *argv[]) case 'n': transfer_count = (size_t)strtol(optarg, NULL, 10); break; + case 'x': + fresh_connect = 1; + break; case 'A': abort_offset = (size_t)strtol(optarg, NULL, 10); break; case 'F': fail_offset = (size_t)strtol(optarg, NULL, 10); break; + case 'M': + max_host_conns = (size_t)strtol(optarg, NULL, 10); + break; case 'P': pause_offset = (size_t)strtol(optarg, NULL, 10); break; case 'r': - resolve = optarg; + resolve = strdup(optarg); break; case 'V': { if(!strcmp("http/1.1", optarg)) @@ -379,7 +389,7 @@ int main(int argc, char *argv[]) curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); - curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); + /* curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); */ curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS); @@ -391,6 +401,8 @@ int main(int argc, char *argv[]) multi_handle = curl_multi_init(); curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); + curl_multi_setopt(multi_handle, CURLMOPT_MAX_HOST_CONNECTIONS, + (long)max_host_conns); active_transfers = 0; for(i = 0; i < transfer_count; ++i) { @@ -406,7 +418,8 @@ int main(int argc, char *argv[]) t = &transfers[i]; t->easy = curl_easy_init(); if(!t->easy || - setup(t->easy, url, t, http_version, host, share, use_earlydata)) { + setup(t->easy, url, t, http_version, host, share, use_earlydata, + fresh_connect)) { fprintf(stderr, "[t-%d] FAILED setup\n", (int)i); return 1; } @@ -486,7 +499,7 @@ int main(int argc, char *argv[]) t->easy = curl_easy_init(); if(!t->easy || setup(t->easy, url, t, http_version, host, share, - use_earlydata)) { + use_earlydata, fresh_connect)) { fprintf(stderr, "[t-%d] FAILED setup\n", (int)i); return 1; } @@ -521,6 +534,8 @@ int main(int argc, char *argv[]) free(transfers); curl_share_cleanup(share); + curl_slist_free_all(host); + free(resolve); return 0; #else diff --git a/tests/http/test_02_download.py b/tests/http/test_02_download.py index fe92df5041..e7910719b3 100644 --- a/tests/http/test_02_download.py +++ b/tests/http/test_02_download.py @@ -633,3 +633,28 @@ class TestDownload: elif proto == 'h3': # not implemented assert earlydata[1] == 0, f'{earlydata}' + + @pytest.mark.parametrize("proto", ['http/1.1', 'h2']) + @pytest.mark.parametrize("max_host_conns", [0, 1, 5]) + def test_02_33_max_host_conns(self, env: Env, httpd, nghttpx, proto, max_host_conns): + if proto == 'h3' and not env.have_h3(): + pytest.skip("h3 not supported") + count = 100 + max_parallel = 100 + docname = 'data-10k' + port = env.port_for(proto) + url = f'https://{env.domain1}:{port}/{docname}' + client = LocalClient(name='hx-download', env=env) + if not client.exists(): + pytest.skip(f'example client not built: {client.name}') + r = client.run(args=[ + '-n', f'{count}', + '-m', f'{max_parallel}', + '-x', # always use a fresh connection + '-M', str(max_host_conns), # limit conns per host + '-r', f'{env.domain1}:{port}:127.0.0.1', + '-V', proto, url + ]) + r.check_exit_code(0) + srcfile = os.path.join(httpd.docs_dir, docname) + self.check_downloads(client, srcfile, count)