Skip to content

Instantly share code, notes, and snippets.

@jay
Last active February 10, 2021 22:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jay/63ad0e64c5abd3dff845b563a760431a to your computer and use it in GitHub Desktop.
Save jay/63ad0e64c5abd3dff845b563a760431a to your computer and use it in GitHub Desktop.
Use libcurl to test synchronous transfers in a multi.
/* Use libcurl to test synchronous transfers in a multi.
Usage: multi_synchronous
This is a demonstration of a repeated synchronous transfer done in a multi
by creating a separate easy handle for each transfer. Normally you would not
do this, you'd use curl_easy_perform in a loop instead.
I forget what curl issue I wrote this in response to.
Copyright (C) 2021 Jay Satiro <raysatiro@yahoo.com>
http://curl.haxx.se/docs/copyright.html
https://gist.github.com/jay/63ad0e64c5abd3dff845b563a760431a
*/
/* !checksrc! disable CPPCOMMENTS all */
#define _CRT_SECURE_NO_WARNINGS
#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
CURL *create_easy()
{
CURL *curl = curl_easy_init();
char *errbuf = (char *)malloc(CURL_ERROR_SIZE);
if(!errbuf)
return NULL;
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
curl_easy_setopt(curl, CURLOPT_PRIVATE, errbuf);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_URL, "https://httpbin.org/get");
//curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1L);
//curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
//curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
return curl;
}
CURLcode multi_synchronous()
{
CURL *easy = NULL;
CURLM *multi = NULL;
CURLMcode mcode = CURLM_OK;
CURLcode result = CURLE_OK;
int still_running = 1;
int count = 0;
int max = 3; /* The number of times to repeat the transfer */
multi = curl_multi_init();
if(!multi) {
fprintf(stderr, "Error: libcurl: curl_multi_init failed.\n");
return CURLE_OUT_OF_MEMORY;
}
while(still_running) {
int qc;
CURLMsg *msg;
/* curl_multi_poll is available in 7.66+ */
if(count) {
mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL);
if(mcode)
break;
}
mcode = curl_multi_perform(multi, &still_running);
if(mcode)
break;
/* Consume any new messages from handles. The only known message at the
time of this writing is CURLMSG_DONE. */
while((msg = curl_multi_info_read(multi, &qc))) {
if(msg->msg == CURLMSG_DONE) {
char *errbuf;
curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &errbuf);
if(msg->data.result) {
fprintf(stderr, "Error: libcurl: (%d) %s\n", msg->data.result,
(errbuf[0] ? errbuf : curl_easy_strerror(msg->data.result)));
result = msg->data.result;
}
/* Make a copy of the easy handle pointer to free it. msg is actually a
pointer to a struct in the easy handle and as documented is not
valid after removing it from the multi or the cleanup functions. */
{
CURL *tmp = msg->easy_handle;
curl_multi_remove_handle(multi, tmp);
curl_easy_cleanup(tmp);
free(errbuf);
if(tmp == easy)
easy = NULL;
}
}
}
/* This block queues a single transfer in the multi only when there are no
longer any transfers in progress (!still_running). Note queued transfers
are transfers in progress. Queued transfers are usually acted on
immediately in curl_multi_perform unless there are libcurl or
user-specified resource constraints.
Further, the previous transfer must have already been marked done and
cleaned up. This is because the example was written to manage only one
transfer at a time (as mentioned this is an example of an unusual case).
Otherwise the easy handles would have to be tracked separately to clean
up their memory on error. That is outside the scope of this example. */
if(!still_running) {
if(easy) {
mcode = CURLM_INTERNAL_ERROR;
break;
}
if(count >= max)
break;
easy = create_easy();
if(!easy) {
fprintf(stderr, "Error: create_easy failed.\n");
result = CURLE_OUT_OF_MEMORY;
break;
}
/* init the extended error buffer. (Not necessary in 7.60+) */
{
char *errbuf;
curl_easy_getinfo(easy, CURLINFO_PRIVATE, &errbuf);
errbuf[0] = 0;
}
fprintf(stderr, "*\n*\n* Adding easy handle to queue.\n*\n*\n");
mcode = curl_multi_add_handle(multi, easy);
if(mcode)
break;
++count;
still_running = 1;
}
}
if(easy) {
char *errbuf;
curl_easy_getinfo(easy, CURLINFO_PRIVATE, &errbuf);
curl_multi_remove_handle(multi, easy);
curl_easy_cleanup(easy);
free(errbuf);
}
if(mcode) {
fprintf(stderr, "Error: libcurl multi interface: (%d) %s\n", mcode,
curl_multi_strerror(mcode));
if(result == CURLE_OK)
result = CURLE_BAD_FUNCTION_ARGUMENT;
}
curl_multi_cleanup(multi);
return result;
}
int main(int argc, char *argv[])
{
CURLcode result;
result = curl_global_init(CURL_GLOBAL_ALL);
if(result) {
fprintf(stderr, "Error: libcurl: (%d) %s\n", result,
curl_easy_strerror(result));
return result;
}
if(atexit(curl_global_cleanup)) {
fprintf(stderr, "Error: atexit failed to register curl_global_cleanup.\n");
return CURLE_FAILED_INIT;
}
result = multi_synchronous();
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment