Last active
February 10, 2021 22:54
-
-
Save jay/63ad0e64c5abd3dff845b563a760431a to your computer and use it in GitHub Desktop.
Use libcurl to test synchronous transfers in a multi.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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 <[email protected]> | |
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