Skip to content

Instantly share code, notes, and snippets.

@jay
Last active September 6, 2026 02:19
Show Gist options
  • Select an option

  • Save jay/12dfe34079d4a425efe8c5a43b40cdd6 to your computer and use it in GitHub Desktop.

Select an option

Save jay/12dfe34079d4a425efe8c5a43b40cdd6 to your computer and use it in GitHub Desktop.
Use libcurl to test multiplexing with CURLOPT_FORBID_REUSE.
/* Use libcurl to test multiplexing with CURLOPT_FORBID_REUSE.
This repro shows that a multiplexed connection with transfers marked forbid
reuse will continue to be used until no transfers are using the connection.
AFAICT the last transfer to complete on the connection (ie connection has no
other transfers using it) determines whether or not the connection is closed
due to forbid reuse. This is discussed in the issue.
Issue: "CURLOPT_FORBID_REUSE.md: has no effect on multiplexed transfers"
https://github.com/curl/curl/pull/22833
gcc -o a multiplex_with_forbid_reuse.c `curl-config --cflags --libs`
* Copyright (C) 2026 Jay Satiro <raysatiro@yahoo.com>
https://curl.haxx.se/docs/copyright.html
https://gist.github.com/jay/12dfe34079d4a425efe8c5a43b40cdd6
*/
#include <stdio.h>
#include <curl/curl.h>
int main(void)
{
CURL *curl1, *curl2;
CURLM *multi;
int curl2_transfer_count;
time_t start;
curl_global_init(CURL_GLOBAL_ALL);
curl1 = curl_easy_init();
curl_easy_setopt(curl1, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl1, CURLOPT_URL, "https://httpbin.org/delay/10");
curl_easy_setopt(curl1, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl1, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl1, CURLOPT_FORBID_REUSE, 1L);
curl2 = curl_easy_init();
curl_easy_setopt(curl2, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl2, CURLOPT_URL, "https://httpbin.org/delay/1");
curl_easy_setopt(curl2, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl2, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl2, CURLOPT_FORBID_REUSE, 1L);
curl2_transfer_count = 0;
multi = curl_multi_init();
curl_multi_add_handle(multi, curl1);
start = time(NULL); /* for testing purposes only, not monotonic */
for(;;) {
struct CURLMsg *m;
int still_running;
if(curl_multi_poll(multi, NULL, 0, 1000, NULL) != CURLM_OK ||
curl_multi_perform(multi, &still_running) != CURLM_OK)
break;
if(!still_running) {
if(curl2_transfer_count == 2) {
/* run the fourth transfer. no other transfers are running.
if the last transfer to complete on the connection (curl1 the 10 sec
transfer) had CURLOPT_FORBID_REUSE on then the connection was closed
and cannot be reused. we expect the fourth transfer will use a new
connection in this case. */
curl_multi_remove_handle(multi, curl2);
curl_multi_add_handle(multi, curl2);
curl2_transfer_count++;
}
else
break;
}
/* at around ~3 seconds run the second transfer. we expect this
transfer will multiplex on the connection in use by curl1. */
if(curl2_transfer_count == 0 && (time(NULL) - start) >= 3) {
fprintf(stderr, "3 seconds\n");
curl_multi_add_handle(multi, curl2);
curl2_transfer_count = 1;
}
do {
int msgq = 0;
m = curl_multi_info_read(multi, &msgq);
if(m && (m->msg == CURLMSG_DONE)) {
CURL *e = m->easy_handle;
if(e == curl2) {
curl_multi_remove_handle(multi, e);
if(curl2_transfer_count == 1) {
/* the second transfer is done so run the third. we expect this
transfer will multiplex on the connection in use by curl1. */
curl_multi_add_handle(multi, e);
curl2_transfer_count++;
}
}
}
} while(m);
}
curl_multi_remove_handle(multi, curl1);
curl_multi_remove_handle(multi, curl2);
curl_easy_cleanup(curl1);
curl_easy_cleanup(curl2);
curl_multi_cleanup(multi);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment