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 2
36
37
38 static const char name[] = "field";
39
40 struct ReadThis {
41 CURL *easy;
42 time_t origin;
43 int count;
44 };
45
46
read_callback(char * ptr,size_t size,size_t nmemb,void * userp)47 static 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)
xferinfo(void * clientp,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)74 static 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
test(char * URL)100 int 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) || 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
254 test_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