Browse Source

curl_multi_get_handles: get easy handles from a multi handle

Closes #11750
Daniel Stenberg 7 months ago
parent
commit
9ffd411735
10 changed files with 113 additions and 1 deletions
  1. 1 0
      docs/libcurl/Makefile.inc
  2. 73 0
      docs/libcurl/curl_multi_get_handles.3
  3. 11 0
      include/curl/multi.h
  4. 1 0
      lib/conncache.c
  5. 1 0
      lib/doh.c
  6. 18 0
      lib/multi.c
  7. 5 1
      lib/urldata.h
  8. 1 0
      libcurl.def
  9. 1 0
      scripts/singleuse.pl
  10. 1 0
      tests/data/test1135

+ 1 - 0
docs/libcurl/Makefile.inc

@@ -73,6 +73,7 @@ man_MANS = \
  curl_multi_assign.3 \
  curl_multi_cleanup.3 \
  curl_multi_fdset.3 \
+ curl_multi_get_handles.3 \
  curl_multi_info_read.3 \
  curl_multi_init.3 \
  curl_multi_perform.3 \

+ 73 - 0
docs/libcurl/curl_multi_get_handles.3

@@ -0,0 +1,73 @@
+.\" **************************************************************************
+.\" *                                  _   _ ____  _
+.\" *  Project                     ___| | | |  _ \| |
+.\" *                             / __| | | | |_) | |
+.\" *                            | (__| |_| |  _ <| |___
+.\" *                             \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" *
+.\" * This software is licensed as described in the file COPYING, which
+.\" * you should have received as part of this distribution. The terms
+.\" * are also available at https://curl.se/docs/copyright.html.
+.\" *
+.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+.\" * copies of the Software, and permit persons to whom the Software is
+.\" * furnished to do so, under the terms of the COPYING file.
+.\" *
+.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+.\" * KIND, either express or implied.
+.\" *
+.\" * SPDX-License-Identifier: curl
+.\" *
+.\" **************************************************************************
+.TH curl_multi_get_handles 3 "28 August 2023" "libcurl" "libcurl"
+.SH NAME
+curl_multi_get_handles - returns all added easy handles
+.SH SYNOPSIS
+.nf
+#include <curl/curl.h>
+
+CURL **curl_multi_get_handles(CURLM *multi_handle);
+.fi
+.SH DESCRIPTION
+Returns an array with pointers to all added easy handles. The end of the list
+is marked with a NULL pointer.
+
+Even if there is not a single easy handle added, this still returns an array
+but with only a single NULL pointer entry.
+
+The returned array contains all the handles that are present at the time of
+the call. As soon as a handle has been removed from or a handle has been added
+to the multi handle after the handle array was returned, the two data points
+are out of sync.
+
+The order of the easy handles within the array is not guaranteed.
+
+The returned array must be freed with a call to \fIcurl_free(3)\fP after use.
+.SH EXAMPLE
+.nf
+  /* init a multi stack */
+  multi_handle = curl_multi_init();
+
+  /* add a transfer */
+  curl_multi_add_handle(multi_handle, http_handle);
+
+  /* extract all added handles */
+  CURL **list = curl_multi_get_handles(multi_handle);
+
+  if(list) {
+    /* remove all added handles */
+    for(i = 0; list[i]; i++) {
+      curl_multi_remove_handle(multi_handle, list[i]);
+    }
+    curl_free(list);
+  }
+.fi
+.SH AVAILABILITY
+Added in 8.4.0
+.SH RETURN VALUE
+Returns NULL on failure. Otherwise it returns a pointer to an allocated array.
+.SH "SEE ALSO"
+.BR curl_multi_cleanup "(3)," curl_multi_init "(3), "
+.BR curl_multi_add_handle "(3), " curl_multi_remove_handle "(3) "

+ 11 - 0
include/curl/multi.h

@@ -426,6 +426,17 @@ CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle,
 CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle,
                                         curl_socket_t sockfd, void *sockp);
 
+/*
+ * Name:    curl_multi_get_handles()
+ *
+ * Desc:    Returns an allocated array holding all handles currently added to
+ *          the multi handle. Marks the final entry with a NULL pointer. If
+ *          there is no easy handle added to the multi handle, this function
+ *          returns an array with the first entry as a NULL pointer.
+ *
+ * Returns: NULL on failure, otherwise a CURL **array pointer
+ */
+CURL_EXTERN CURL **curl_multi_get_handles(CURLM *multi_handle);
 
 /*
  * Name: curl_push_callback

+ 1 - 0
lib/conncache.c

@@ -107,6 +107,7 @@ int Curl_conncache_init(struct conncache *connc, int size)
   connc->closure_handle = curl_easy_init();
   if(!connc->closure_handle)
     return 1; /* bad */
+  connc->closure_handle->internal = true;
 
   Curl_hash_init(&connc->hash, size, Curl_hash_str,
                  Curl_str_key_compare, free_bundle_hash_entry);

+ 1 - 0
lib/doh.c

@@ -242,6 +242,7 @@ static CURLcode dohprobe(struct Curl_easy *data,
     /* pass in the struct pointer via a local variable to please coverity and
        the gcc typecheck helpers */
     struct dynbuf *resp = &p->serverdoh;
+    doh->internal = true;
     ERROR_CHECK_SETOPT(CURLOPT_URL, url);
     ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);

+ 18 - 0
lib/multi.c

@@ -3788,3 +3788,21 @@ unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi)
   DEBUGASSERT(multi);
   return multi->max_concurrent_streams;
 }
+
+struct Curl_easy **curl_multi_get_handles(struct Curl_multi *multi)
+{
+  struct Curl_easy **a = malloc(sizeof(struct Curl_easy *) *
+                                (multi->num_easy + 1));
+  if(a) {
+    int i = 0;
+    struct Curl_easy *e = multi->easyp;
+    while(e) {
+      DEBUGASSERT(i < multi->num_easy);
+      if(!e->internal)
+        a[i++] = e;
+      e = e->next;
+    }
+    a[i] = NULL; /* last entry is a NULL */
+  }
+  return a;
+}

+ 5 - 1
lib/urldata.h

@@ -1950,7 +1950,7 @@ struct Curl_easy {
      other using the same cache. For easier tracking
      in log output.
      This may wrap around after LONG_MAX to 0 again, so it
-     has no uniqueness guarantuee for very large processings. */
+     has no uniqueness guarantee for very large processings. */
   curl_off_t id;
 
   /* first, two fields for the linked list of these */
@@ -2013,6 +2013,10 @@ struct Curl_easy {
 #ifdef USE_HYPER
   struct hyptransfer hyp;
 #endif
+
+  /* internal: true if this easy handle was created for internal use and the
+     user does not have ownership of the handle. */
+  bool internal;
 };
 
 #define LIBCURL_NAME "libcurl"

+ 1 - 0
libcurl.def

@@ -51,6 +51,7 @@ curl_multi_add_handle
 curl_multi_assign
 curl_multi_cleanup
 curl_multi_fdset
+curl_multi_get_handles
 curl_multi_info_read
 curl_multi_init
 curl_multi_perform

+ 1 - 0
scripts/singleuse.pl

@@ -96,6 +96,7 @@ my %api = (
     'curl_multi_assign' => 'API',
     'curl_multi_cleanup' => 'API',
     'curl_multi_fdset' => 'API',
+    'curl_multi_get_handles' => 'API',
     'curl_multi_info_read' => 'API',
     'curl_multi_init' => 'API',
     'curl_multi_perform' => 'API',

+ 1 - 0
tests/data/test1135

@@ -106,6 +106,7 @@ curl_multi_socket_all
 curl_multi_timeout
 curl_multi_setopt
 curl_multi_assign
+curl_multi_get_handles
 curl_pushheader_bynum
 curl_pushheader_byname
 curl_easy_option_by_name