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