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