Closed
Description
I did this
/* Proof of Concept for libcurl 8.3.0 bug where paused downloads
* with unknown Content-Length suddendly get errornously reported
* as completed.
*
* Issue discovered and PoC written by Harry Sintonen <sintonen@iki.fi>
*
* Loosely based on https://curl.se/libcurl/c/multi-app.html
*/
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <curl/curl.h>
#define HANDLECOUNT 2
int err(void)
{
fprintf(stderr, "something unexpected went wrong - bailing out!\n");
exit(2);
}
struct handle
{
int idx;
int paused;
CURL *h;
};
static size_t cb(void *data, size_t size, size_t nmemb, void *clientp)
{
size_t realsize = size * nmemb;
struct handle *handle = (struct handle *) clientp;
curl_off_t totalsize;
printf("handle %d write %lu bytes\n", handle->idx, realsize);
if(curl_easy_getinfo(handle->h, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &totalsize) == CURLE_OK)
printf("handle %d Content-Length %"CURL_FORMAT_CURL_OFF_T"\n", handle->idx, totalsize);
handle->paused = 1;
return CURL_WRITEFUNC_PAUSE;
}
int main(void)
{
struct handle handles[HANDLECOUNT];
CURLM *multi_handle;
int i, still_running = 1, msgs_left, numfds;
CURLMsg *msg;
int rounds = 0;
int rc = 1;
curl_global_init(CURL_GLOBAL_DEFAULT);
for(i = 0; i<HANDLECOUNT; i++) {
handles[i].idx = i;
handles[i].paused = 0;
handles[i].h = curl_easy_init();
if(!handles[i].h ||
curl_easy_setopt(handles[i].h, CURLOPT_WRITEFUNCTION, cb) != CURLE_OK ||
curl_easy_setopt(handles[i].h, CURLOPT_WRITEDATA, &handles[i]) != CURLE_OK ||
curl_easy_setopt(handles[i].h, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK ||
curl_easy_setopt(handles[i].h, CURLOPT_URL,
"https://www.meteoblue.com/en") != CURLE_OK) {
err();
}
}
multi_handle = curl_multi_init();
if (!multi_handle)
err();
for(i = 0; i<HANDLECOUNT; i++) {
if(curl_multi_add_handle(multi_handle, handles[i].h) != CURLM_OK)
err();
}
for(;;) {
if(curl_multi_perform(multi_handle, &still_running) != CURLM_OK)
err();
if(!still_running)
break;
if(curl_multi_poll(multi_handle, NULL, 0, 100, &numfds) != CURLM_OK)
err();
while((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
if(msg->msg == CURLMSG_DONE) {
for(i = 0; i<HANDLECOUNT; i++) {
if(msg->easy_handle == handles[i].h) {
printf("handle %d done, result %d - wtf?\n", i, msg->data.result);
goto out;
}
}
}
}
/* Successfully paused? */
if(handles[0].paused && handles[1].paused && ++rounds > 10) {
/* Both handles are paused and transfer hasn't completed */
printf("the test program ran as expected\n");
rc = 0;
break;
}
}
out:
for(i = 0; i<HANDLECOUNT; i++) {
curl_multi_remove_handle(multi_handle, handles[i].h);
curl_easy_cleanup(handles[i].h);
}
curl_multi_cleanup(multi_handle);
curl_global_cleanup();
return rc;
}
I expected the following
Both connections to get paused and neither complete.
curl/libcurl version
libcurl 8.3.0
operating system
Linux hostname 6.3.0-2-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.3.11-1 (2023-07-01) x86_64 GNU/Linux