1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25#if !defined(LIB670) && !defined(LIB671) 26#define CURL_DISABLE_DEPRECATION /* Using and testing the form api */ 27#endif 28 29#include "test.h" 30 31#include <time.h> 32 33#include "memdebug.h" 34 35#define PAUSE_TIME 5 36 37 38static const char name[] = "field"; 39 40struct ReadThis { 41 CURL *easy; 42 time_t origin; 43 int count; 44}; 45 46 47static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *userp) 48{ 49 struct ReadThis *pooh = (struct ReadThis *) userp; 50 time_t delta; 51 52 if(size * nmemb < 1) 53 return 0; 54 55 switch(pooh->count++) { 56 case 0: 57 *ptr = '\x41'; /* ASCII A. */ 58 return 1; 59 case 1: 60 pooh->origin = time(NULL); 61 return CURL_READFUNC_PAUSE; 62 case 2: 63 delta = time(NULL) - pooh->origin; 64 *ptr = delta >= PAUSE_TIME? '\x42': '\x41'; /* ASCII A or B. */ 65 return 1; 66 case 3: 67 return 0; 68 } 69 fprintf(stderr, "Read callback called after EOF\n"); 70 exit(1); 71} 72 73#if !defined(LIB670) && !defined(LIB672) 74static int xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, 75 curl_off_t ultotal, curl_off_t ulnow) 76{ 77 struct ReadThis *pooh = (struct ReadThis *) clientp; 78 79 (void) dltotal; 80 (void) dlnow; 81 (void) ultotal; 82 (void) ulnow; 83 84 if(pooh->origin) { 85 time_t delta = time(NULL) - pooh->origin; 86 87 if(delta >= 4 * PAUSE_TIME) { 88 fprintf(stderr, "unpausing failed: drain problem?\n"); 89 return CURLE_ABORTED_BY_CALLBACK; 90 } 91 92 if(delta >= PAUSE_TIME) 93 curl_easy_pause(pooh->easy, CURLPAUSE_CONT); 94 } 95 96 return 0; 97} 98#endif 99 100int test(char *URL) 101{ 102#if defined(LIB670) || defined(LIB671) 103 curl_mime *mime = NULL; 104 curl_mimepart *part; 105#else 106 CURLFORMcode formrc; 107 struct curl_httppost *formpost = NULL; 108 struct curl_httppost *lastptr = NULL; 109#endif 110#if defined(LIB670) || defined(LIB672) 111 CURLM *multi = NULL; 112 CURLMcode mres; 113 CURLMsg *msg; 114 int msgs_left; 115 int still_running = 0; 116#endif 117 118 struct ReadThis pooh; 119 CURLcode result; 120 int res = TEST_ERR_FAILURE; 121 122 /* 123 * Check proper pausing/unpausing from a mime or form read callback. 124 */ 125 126 if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { 127 fprintf(stderr, "curl_global_init() failed\n"); 128 return TEST_ERR_MAJOR_BAD; 129 } 130 131 pooh.origin = (time_t) 0; 132 pooh.count = 0; 133 pooh.easy = curl_easy_init(); 134 135 /* First set the URL that is about to receive our POST. */ 136 test_setopt(pooh.easy, CURLOPT_URL, URL); 137 138 /* get verbose debug output please */ 139 test_setopt(pooh.easy, CURLOPT_VERBOSE, 1L); 140 141 /* include headers in the output */ 142 test_setopt(pooh.easy, CURLOPT_HEADER, 1L); 143 144#if defined(LIB670) || defined(LIB671) 145 /* Build the mime tree. */ 146 mime = curl_mime_init(pooh.easy); 147 part = curl_mime_addpart(mime); 148 result = curl_mime_name(part, name); 149 if(result) { 150 fprintf(stderr, 151 "Something went wrong when building the mime structure: %d\n", 152 (int) result); 153 goto test_cleanup; 154 } 155 156 res = curl_mime_data_cb(part, (curl_off_t) 2, read_callback, 157 NULL, NULL, &pooh); 158 159 /* Bind mime data to its easy handle. */ 160 if(!res) 161 test_setopt(pooh.easy, CURLOPT_MIMEPOST, mime); 162#else 163 /* Build the form. */ 164 formrc = curl_formadd(&formpost, &lastptr, 165 CURLFORM_COPYNAME, name, 166 CURLFORM_STREAM, &pooh, 167 CURLFORM_CONTENTLEN, (curl_off_t) 2, 168 CURLFORM_END); 169 if(formrc) { 170 fprintf(stderr, "curl_formadd() = %d\n", (int) formrc); 171 goto test_cleanup; 172 } 173 174 /* We want to use our own read function. */ 175 test_setopt(pooh.easy, CURLOPT_READFUNCTION, read_callback); 176 177 /* Send a multi-part formpost. */ 178 test_setopt(pooh.easy, CURLOPT_HTTPPOST, formpost); 179#endif 180 181#if defined(LIB670) || defined(LIB672) 182 /* Use the multi interface. */ 183 multi = curl_multi_init(); 184 mres = curl_multi_add_handle(multi, pooh.easy); 185 while(!mres) { 186 struct timeval timeout; 187 int rc = 0; 188 fd_set fdread; 189 fd_set fdwrite; 190 fd_set fdexcept; 191 int maxfd = -1; 192 193 mres = curl_multi_perform(multi, &still_running); 194 if(!still_running || mres != CURLM_OK) 195 break; 196 197 if(pooh.origin) { 198 time_t delta = time(NULL) - pooh.origin; 199 200 if(delta >= 4 * PAUSE_TIME) { 201 fprintf(stderr, "unpausing failed: drain problem?\n"); 202 res = CURLE_OPERATION_TIMEDOUT; 203 break; 204 } 205 206 if(delta >= PAUSE_TIME) 207 curl_easy_pause(pooh.easy, CURLPAUSE_CONT); 208 } 209 210 FD_ZERO(&fdread); 211 FD_ZERO(&fdwrite); 212 FD_ZERO(&fdexcept); 213 timeout.tv_sec = 0; 214 timeout.tv_usec = 1000000 * PAUSE_TIME / 10; 215 mres = curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcept, &maxfd); 216 if(mres) 217 break; 218#if defined(_WIN32) 219 if(maxfd == -1) 220 Sleep(100); 221 else 222#endif 223 rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcept, &timeout); 224 if(rc == -1) { 225 fprintf(stderr, "Select error\n"); 226 break; 227 } 228 } 229 230 if(mres != CURLM_OK) 231 for(;;) { 232 msg = curl_multi_info_read(multi, &msgs_left); 233 if(!msg) 234 break; 235 if(msg->msg == CURLMSG_DONE) { 236 result = msg->data.result; 237 res = (int) result; 238 } 239 } 240 241 curl_multi_remove_handle(multi, pooh.easy); 242 curl_multi_cleanup(multi); 243 244#else 245 /* Use the easy interface. */ 246 test_setopt(pooh.easy, CURLOPT_XFERINFODATA, &pooh); 247 test_setopt(pooh.easy, CURLOPT_XFERINFOFUNCTION, xferinfo); 248 test_setopt(pooh.easy, CURLOPT_NOPROGRESS, 0L); 249 result = curl_easy_perform(pooh.easy); 250 res = (int) result; 251#endif 252 253 254test_cleanup: 255 curl_easy_cleanup(pooh.easy); 256#if defined(LIB670) || defined(LIB671) 257 curl_mime_free(mime); 258#else 259 curl_formfree(formpost); 260#endif 261 262 curl_global_cleanup(); 263 return res; 264} 265