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.haxx.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