From 87869e38d7afdec3ef1bb4965711458b088e254f Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Tue, 21 Jan 2020 01:33:04 +0100 Subject: mime: latch last read callback status. In case a read callback returns a status (pause, abort, eof, error) instead of a byte count, drain the bytes read so far but remember this status for further processing. Takes care of not losing data when pausing, and properly resume a paused mime structure when requested. New tests 670-673 check unpausing cases, with easy or multi interface and mime or form api. Fixes #4813 Reported-by: MrdUkk on github Closes #4833 --- tests/libtest/lib670.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 tests/libtest/lib670.c (limited to 'tests/libtest/lib670.c') diff --git a/tests/libtest/lib670.c b/tests/libtest/lib670.c new file mode 100644 index 000000000..62e22623e --- /dev/null +++ b/tests/libtest/lib670.c @@ -0,0 +1,240 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include + +#include "test.h" + +#include "memdebug.h" + +#define PAUSE_TIME 2 + + +static const char name[] = "field"; + +struct ReadThis { + CURL *easy; + time_t origin; + int count; +}; + + +static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *userp) +{ + struct ReadThis *pooh = (struct ReadThis *) userp; + time_t delta; + + if(size * nmemb < 1) + return 0; + + switch(pooh->count++) { + case 0: + *ptr = '\x41'; /* ASCII A. */ + return 1; + case 1: + pooh->origin = time(NULL); + return CURL_READFUNC_PAUSE; + case 2: + delta = time(NULL) - pooh->origin; + *ptr = delta >= PAUSE_TIME? '\x42': '\x41'; /* ASCII A or B. */ + return 1; + case 3: + return 0; + } + fprintf(stderr, "Read callback called after EOF\n"); + exit(1); +} + +#if !defined(LIB670) && !defined(LIB672) +static int xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, + curl_off_t ultotal, curl_off_t ulnow) +{ + struct ReadThis *pooh = (struct ReadThis *) clientp; + + (void) dltotal; + (void) dlnow; + (void) ultotal; + (void) ulnow; + + if(pooh->origin && time(NULL) - pooh->origin >= PAUSE_TIME) + curl_easy_pause(pooh->easy, CURLPAUSE_CONT); + + return 0; +} +#endif + +int test(char *URL) +{ +#if defined(LIB670) || defined(LIB671) + curl_mime *mime = NULL; + curl_mimepart *part; +#else + CURLFORMcode formrc; + struct curl_httppost *formpost = NULL; + struct curl_httppost *lastptr = NULL; +#endif +#if defined(LIB670) || defined(LIB672) + CURLM *multi = NULL; + CURLMcode mres; + CURLMsg *msg; + int msgs_left; + int still_running = 0; +#endif + + struct ReadThis pooh; + CURLcode result; + int res = TEST_ERR_FAILURE; + + /* + * Check proper pausing/unpausing from a mime or form read callback. + */ + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + fprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + pooh.origin = (time_t) 0; + pooh.count = 0; + pooh.easy = curl_easy_init(); + + /* First set the URL that is about to receive our POST. */ + test_setopt(pooh.easy, CURLOPT_URL, URL); + + /* get verbose debug output please */ + test_setopt(pooh.easy, CURLOPT_VERBOSE, 1L); + + /* include headers in the output */ + test_setopt(pooh.easy, CURLOPT_HEADER, 1L); + +#if defined(LIB670) || defined(LIB671) + /* Build the mime tree. */ + mime = curl_mime_init(pooh.easy); + part = curl_mime_addpart(mime); + result = curl_mime_name(part, name); + if(!result) + res = curl_mime_data_cb(part, (curl_off_t) 2, read_callback, + NULL, NULL, &pooh); + + if(result) { + fprintf(stderr, + "Something went wrong when building the mime structure: %d\n", + (int) result); + goto test_cleanup; + } + + /* Bind mime data to its easy handle. */ + if(!res) + test_setopt(pooh.easy, CURLOPT_MIMEPOST, mime); +#else + /* Build the form. */ + formrc = curl_formadd(&formpost, &lastptr, + CURLFORM_COPYNAME, name, + CURLFORM_STREAM, &pooh, + CURLFORM_CONTENTLEN, (curl_off_t) 2, + CURLFORM_END); + if(formrc) { + fprintf(stderr, "curl_formadd() = %d\n", (int) formrc); + goto test_cleanup; + } + + /* We want to use our own read function. */ + test_setopt(pooh.easy, CURLOPT_READFUNCTION, read_callback); + + /* Send a multi-part formpost. */ + test_setopt(pooh.easy, CURLOPT_HTTPPOST, formpost); +#endif + +#if defined(LIB670) || defined(LIB672) + /* Use the multi interface. */ + multi = curl_multi_init(); + mres = curl_multi_add_handle(multi, pooh.easy); + while(!mres) { + struct timeval timeout; + int rc = 0; + fd_set fdread; + fd_set fdwrite; + fd_set fdexcept; + int maxfd = -1; + + mres = curl_multi_perform(multi, &still_running); + if(!still_running || mres != CURLM_OK) + break; + + if(pooh.origin && time(NULL) - pooh.origin >= PAUSE_TIME) + curl_easy_pause(pooh.easy, CURLPAUSE_CONT); + + FD_ZERO(&fdread); + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcept); + timeout.tv_sec = 0; + timeout.tv_usec = 1000000 * PAUSE_TIME / 10; + mres = curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcept, &maxfd); + if(mres) + break; +#ifdef _WIN32 + if(maxfd == -1) + Sleep(100); + else +#endif + rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcept, &timeout); + if(rc == -1) { + fprintf(stderr, "Select error\n"); + break; + } + } + + if(mres != CURLM_OK) + for(;;) { + msg = curl_multi_info_read(multi, &msgs_left); + if(!msg) + break; + if(msg->msg == CURLMSG_DONE) { + result = msg->data.result; + res = (int) result; + } + } + + curl_multi_remove_handle(multi, pooh.easy); + curl_multi_cleanup(multi); + +#else + /* Use the easy interface. */ + test_setopt(pooh.easy, CURLOPT_XFERINFODATA, &pooh); + test_setopt(pooh.easy, CURLOPT_XFERINFOFUNCTION, xferinfo); + test_setopt(pooh.easy, CURLOPT_NOPROGRESS, 0L); + result = curl_easy_perform(pooh.easy); + res = (int) result; +#endif + + +test_cleanup: + curl_easy_cleanup(pooh.easy); +#if defined(LIB670) || defined(LIB671) + curl_mime_free(mime); +#else + curl_formfree(formpost); +#endif + + curl_global_cleanup(); + return res; +} -- cgit v1.2.3