Skip to content

Instantly share code, notes, and snippets.

@jay

jay/ListEasyHandles.c

Last active Feb 3, 2021
Embed
What would you like to do?
Use libcurl to add some easy handles to a multi handle, then list them.
/* Use libcurl to add some easy handles to a multi handle, then list them.
Usage: ListEasyHandles
curl-library mailing list thread:
'How to list easy handles from a multi handle'
https://curl.haxx.se/mail/lib-2016-06/0000.html
* Copyright (C) 2016 Jay Satiro <raysatiro@yahoo.com>
https://curl.se/docs/copyright.html
https://gist.github.com/jay/5b68c1bde985b1b2d23f2b583a26e5ad
*/
#define _CRT_NONSTDC_NO_DEPRECATE
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
/* https://curl.se/download.html */
#include <curl/curl.h>
#undef FALSE
#define FALSE 0
#undef TRUE
#define TRUE 1
/* To access the linked list of easy handles in a multi handle we are accessing
the internal multi and easy libcurl structs that are different depending on
the version. Starting in 7.75.0 the relevant part of the structs changed so
older versions are no longer supported. Older revisions of this example are
compatible with older versions of libcurl. To support versions later than
7.75.0 would require reviewing the structs of every release since then
to see if the layout of the members used has changed.
CURL (internal: Curl_easy, or SessionHandle prior to 7.50.0)
CURLM (internal: Curl_multi)
https://github.com/curl/curl/blob/curl-7_75_0/lib/urldata.h#L1885-L1892
https://github.com/curl/curl/blob/curl-7_75_0/lib/multihandle.h#L83-L89
Version is in hex, it is a 24 bit number created like this:
<8 bits major number> | <8 bits minor number> | <8 bits patch number>
*/
#define MIN_SUPPORTED_LIBCURL_VER 0x074B00 /* 7.75.0 */
#define MAX_SUPPORTED_LIBCURL_VER 0x074B00 /* 7.75.0 */
struct curl_pseudo_easy {
unsigned int magic;
struct curl_pseudo_easy *next;
struct curl_pseudo_easy *prev;
};
struct curl_pseudo_multi {
unsigned int magic;
struct curl_pseudo_easy *easy;
};
/* This list is a copy of the easy handle pointers in a multi handle. */
struct easy_handle_list {
struct easy_handle_list *next;
CURL *easy;
};
void free_easy_handle_list(struct easy_handle_list *list)
{
while(list) {
struct easy_handle_list *next = list->next;
free(list);
list = next;
}
return;
}
/*
Copy the easy handle pointers in multi to *list.
*list may be NULL even on success: that means no easy handles are in multi.
Free *list by calling free_easy_handle_list.
Returns TRUE on success or FALSE on failure.
*/
int GetEasyHandles(CURLM *multi, struct easy_handle_list **list)
{
CURL *easy;
struct curl_pseudo_multi multi_container;
struct curl_pseudo_easy easy_container;
struct easy_handle_list *prev_item;
*list = NULL;
memcpy(&multi_container, multi, sizeof(multi_container));
easy = multi_container.easy;
prev_item = NULL;
while(easy) {
struct easy_handle_list *item = calloc(1, sizeof(*item));
if(!item) {
free_easy_handle_list(*list);
*list = NULL;
return FALSE;
}
item->easy = easy;
if(prev_item)
prev_item->next = item;
else
*list = item;
/* get the next easy handle in the multi handle */
memcpy(&easy_container, easy, sizeof(easy_container));
easy = easy_container.next;
if(!easy || easy == multi_container.easy)
break;
prev_item = item;
}
return TRUE;
}
/*
Show the address of every easy handle in a multi handle.
Returns TRUE on success or FALSE on failure.
*/
int ListEasyHandles(CURLM *multi)
{
struct easy_handle_list *list, *p;
if(!GetEasyHandles(multi, &list)) {
fprintf(stderr, "Error: GetEasyHandles() failed.\n");
return FALSE;
}
if(!list) {
printf("Found no easy handles in multi handle %p.\n", multi);
return TRUE;
}
for(p = list; p; p = p->next) {
printf("Found easy handle %p in multi handle %p.\n", p->easy, multi);
}
free_easy_handle_list(list);
return TRUE;
}
int main(int argc, char *argv[])
{
CURL *easy[3];
CURLM *multi;
unsigned int i, libcurl_version_num;
(void)argc, (void)argv;
if(curl_global_init(CURL_GLOBAL_ALL)) {
fprintf(stderr, "Fatal: The initialization of libcurl has failed.\n");
return EXIT_FAILURE;
}
if(atexit(curl_global_cleanup)) {
fprintf(stderr, "Fatal: atexit failed to register curl_global_cleanup.\n");
curl_global_cleanup();
return EXIT_FAILURE;
}
libcurl_version_num = curl_version_info(CURLVERSION_NOW)->version_num;
if(libcurl_version_num < MIN_SUPPORTED_LIBCURL_VER ||
libcurl_version_num > MAX_SUPPORTED_LIBCURL_VER) {
fprintf(stderr, "Fatal: libcurl version not supported, check that the "
"pseudo structs are supported by your libcurl.\n");
return EXIT_FAILURE;
}
multi = curl_multi_init();
if(!multi) {
fprintf(stderr, "Fatal: Couldn't create a multi handle.\n");
return EXIT_FAILURE;
}
for(i = 0; i < sizeof(easy) / sizeof(easy[0]); ++i) {
easy[i] = curl_easy_init();
if(!easy[i]) {
fprintf(stderr, "Fatal: Couldn't create an easy handle.\n");
return EXIT_FAILURE;
}
curl_multi_add_handle(multi, easy[i]);
printf("Added easy handle %p to multi handle %p.\n", easy[i], multi);
}
if(!ListEasyHandles(multi)) {
fprintf(stderr, "Fatal: ListEasyHandles() failed.\n");
return EXIT_FAILURE;
}
curl_multi_cleanup(multi);
for(i = 0; i < sizeof(easy) / sizeof(easy[0]); ++i) {
curl_easy_cleanup(easy[i]);
}
return EXIT_SUCCESS;
}
@jeroen

This comment has been minimized.

Copy link

@jeroen jeroen commented Apr 17, 2018

Was this ever merged into libcurl?

@jay

This comment has been minimized.

Copy link
Owner Author

@jay jay commented Jan 8, 2019

@jeroen No since the method relies on internal structures that are subject to change. I didn't see your comment until just now by happenstance, it's possible GitHub doesn't notify of gist comments or I missed the notification.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment