]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
pytest: add test for use of CURLMOPT_MAX_HOST_CONNECTIONS
authorStefan Eissing <stefan@eissing.org>
Wed, 6 Nov 2024 11:42:34 +0000 (12:42 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 2 Dec 2024 20:16:10 +0000 (21:16 +0100)
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

tests/http/clients/hx-download.c
tests/http/test_02_download.py

index de50e273e2bcf6f65216e83de365d7d26d303ba4..94e9d0cbd6d43b3e00c321e2521f047f2b17a54a 100644 (file)
@@ -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
index fe92df5041a629e987b9368aeabb16b16eaa3b42..e7910719b3ebdcddfa9cbc6d6028201c82ecb332 100644 (file)
@@ -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)